目次
前
次
シミュレータ作成
マイコンで実現するPLCを動かす前に、シミュレータを
利用して、動作を確認します。
ニモニック形式になったシーケンス制御コードを読み込み
ラダー図の1行を、逐次実行します。
1スキャンの動作を確認します。こうすると
16ビットの入力を任意に設定できるようにし
入力変化があったときの動作を検証できます。
マルチプラットホームで動作するように、Tcl/Tkで
シミュレータを作成します。
シミュレータ作成が、同時に1スキャン処理の
インタプリタ動作のテストであり、デバッグに
なります。
シミュレータを作成することで、論理上の矛盾と
小さなバグを修正して、Cのソースコード改修が
できました。
画面構成
画面構成を決めます。
シミュレーションのフローは、以下としました。
- テキストファイルからニモニック形式ラダー情報入力
- ニモニックから内部情報形式に変換
- 入力の初期状態指定
- ZEROボタンでシミュレーション開始点をスキャン先頭に設定
- STEPボタンで1ライン動作
シミュレーションのフローから、画面構成を次のように
しました。
Exitボタンをクリックすると、シミュレータを終了
して、OS(Operating System)の制御下に戻ります。
Loadボタンをクリックすると、テキストファイルから
ニモニック形式ラダー情報入力します。
ニモニック形式ラダー情報入力すると、同時に内部情報
形式に変換してしまいます。また、内部メモリの状態を
2進で表示します。
ニモニック形式ラダー情報と内部情報は、対比できるよう
並べて表示しています。
インタプリタは、内部メモリの内容を操作するだけなので
入力に割当てたメモリ状態を初期化し、シミュレーション
します。
内部メモリには、1ビットごとにアドレスを割当てます。
ビットアドレス0〜15を入力に、ビットアドレス16〜31を
出力にするので、同時に分けて表示します。
入力メモリの状態変更のため、SETボタンを用意しました。
0〜15のビットアドレスおよび0か1の値を指定して、SET
ボタンをクリックすると、該当ビットの内容を変更します。
ZEROボタンでシミュレーション開始点をスキャン
の先頭に設定します。
STEPボタンで、ニモニックを1ラインごとに実行
していきます。
LD、LDN命令で取り出した値はregに表示します。
AND、ANDN、OR、ORD命令で指定するビットアドレスの
内容はregxに表示します。
論理演算結果は、regに表示します。
ANDB、ORB命令は、シフトレジスタで作ったスタックに
保存している値をregxに取出し、regと論理演算します。
論理演算結果は、regに保存します。
オブジェクト定義
画面構成を決めたので、オブジェクトを定義します。
ニモニック情報入力と内部情報へ変換した内容を
表示するリストボックスを定義します。
listbox .lstLadder -yscrollcommand ".scbLadderV set"
listbox .lstLCode -yscrollcommand ".scbLCodeV set"
スクロールボックスをつけ、スクロールで全体が見える
ようにします。
scrollbar .scbLadderV -orient vertical -command ".lstLadder yview"
scrollbar .scbLCodeV -orient vertical -command ".lstLCode yview"
スキャン処理をSTEP実行していくと、内部メモリの内容が
変化します。内部メモリ、入力メモリ、出力メモリの内容
を表示するリストボックスを定義します。
listbox .lstMemory -yscrollcommand ".scbMemoryV set"
listbox .lstInput -yscrollcommand ".scbInputV set"
listbox .lstOutput -yscrollcommand ".scbOutputV set"
スクロールボックスをつけ、スクロールで全体が見える
ようにします。
scrollbar .scbMemoryV -orient vertical -command ".lstMemory yview"
scrollbar .scbInputV -orient vertical -command ".lstInput yview"
scrollbar .scbOutputV -orient vertical -command ".lstOutput yview"
次の4ボタンを定義します。
- .btnLoad テキストファイルからニモニック情報を入力
- .btnSet 内部メモリの状態設定
- .btnZero スキャン開始を0ラインに設定
- .btnStep 1ライン動作指定
button .btnLoad -text "Load"
button .btnSet -text "Set"
button .btnZero -text "Zero"
button .btnStep -text "Step"
ボタンをクリックしたときの処理は、別に定義します。
内部メモリの内容を、1ビットごとに設定できるように
2つのスピンボックスを定義します。
- .sbxRange ビットアドレス(0〜15)を指定
- .sbxVal ビットの状態設定
spinbox .sbxRange -from 0 -to 15 -state readonly -width 4
spinbox .sbxVal -value {"0" "1"} -state readonly -width 4
変数定義
シミュレータはニモニック形式情報を、内部情報に変換して
から、ラダー図の1ラインごとに内部メモリの内容を取出し
変更していきます。
ニモニック形式情報と内部情報を格納する変数を、各々
pgm
pgmx
とします。
また、テキストファイル名を指定しなければならないので
テキストファイル名を格納する変数を
theFileName
とします。
内部メモリの状態を格納する変数が必要になるので
memory
とします。
また、タイマー、カウンタが必要なので
mtim
ctim
とします。
タイマー、カウンタは、それぞれ16個用意します。
ニモニックとの対応は、次のようにしました。
mtim(0) : T000
mtim(1) : T001
mtim(2) : T002
mtim(3) : T003
mtim(4) : T004
mtim(5) : T005
mtim(6) : T006
mtim(7) : T007
mtim(8) : T100
mtim(9) : T101
mtim(10) : T102
mtim(11) : T103
mtim(12) : T104
mtim(13) : T105
mtim(14) : T106
mtim(15) : T107
カウンタは、次の対応とします。
ctim(0) : C000
ctim(1) : C001
ctim(2) : C002
ctim(3) : C003
ctim(4) : C004
ctim(5) : C005
ctim(6) : C006
ctim(7) : C007
ctim(8) : C100
ctim(9) : C101
ctim(10) : C102
ctim(11) : C103
ctim(12) : C104
ctim(13) : C105
ctim(14) : C106
ctim(15) : C107
ここまで、変数を定義したので、実際のコード作成で
必要な変数を付加していきます。
テキストファイル処理
テキストファイルに含まれている、ニモニック形式情報を
内部変数に格納します。
Loadボタンをクリックしたときの動作です。
処理の流れは、以下とします。
- テキストファイル名入力
- リストボックスをクリア
- ファイルオープン
- ファイルから1行リードし、変数pgmに格納
- 変数pgmの内容をリストボックスに追加して表示
- 変数pgmの内容を、内部情報に変換後、変数pgmxに保存
- 変数pgmxの内容をリストボックスに追加して表示
- EOFを検出するまで、上記4処理を繰り返す
- ファイルクローズ
フローから、Loadボタンの処理を次のように定義
しました。
.btnLoad configure -command {
set theFileName [tk_getOpenFile -filetypes {{"text file" {.txt}}}]
# clear list box
.lstLadder delete 0 end
.lstLCode delete 0 end
# open
set fd_in [open $theFileName "r"]
# read context
set i 0
while { [gets $fd_in sbuf] >= 0 } {
# store
set pgm($i) $sbuf
# display
.lstLadder insert end $pgm($i)
# convert
set pgmx($i) [getCode $pgm($i)]
# display
.lstLCode insert end $pgmx($i)
# increment
incr i
}
set last $i
# close
close $fd_in
ShowMemory 0
}
変数pgm、pgmxは配列として、配列の管理用変数
iとlastを用意しました。lastは、インタプリタ
で1スキャン処理の終了を判定するために利用
します。
ニモニック情報を内部情報に変換するために、getCodeを
使います。getCodeは、命令とビットアドレスを判断して
4つの英数字を生成します。
単純に、コード変換する手続きです。
proc getCode {x} {
# get command
set cmd [lindex $x 0]
# get data
set dat [lindex $x end]
if { $cmd == $dat } {
set dath 0
set datl 0
} else {
# command SETC or SETT
if { $cmd == "SETC" || $cmd == "SETT" } {
# get counter number string
set cname [lindex $x 1]
# calculate value
set cname [format "%x" [getRegNumber $cname]]
# get counter value string
set dat [lindex $x end]
}
# command INC or DEC
if { $cmd == "INC" || $cmd == "DEC" } {
# get counter number string
set cname [lindex $x end]
# calculate value
set cname [format "%x" [getRegNumber $cname]]
# get counter value string
set dat 0
}
# command COND
if { $cmd == "COND" } {
# get counter number string
set cname [lindex $x 1]
# calculate value
set cname [format "%x" [getRegNumber $cname]]
# get relay value string
set rname [lindex $x end]
set dat [getRegNumber $rname]
}
# command JUMP
if { $cmd == "JUMP" } {
# get counter number string
set cname [lindex $x end]
# convert
set dh [expr $cname / 256]
set dl [expr $cname % 256]
# copy
set dat $dl
# separate
set dl [expr $dh % 16]
}
# convert
set dath [format "%x" [expr $dat / 16]]
set datl [format "%x" [expr $dat % 16]]
}
# conversion
if { $cmd == "END" } {
set xcode "0000"
}
if { $cmd == "OUT" } {
set xcode "10$dath$datl"
}
if { $cmd == "LD" } {
set xcode "20$dath$datl"
}
if { $cmd == "LDN" } {
set xcode "30$dath$datl"
}
if { $cmd == "AND" } {
set xcode "40$dath$datl"
}
if { $cmd == "ANDN" } {
set xcode "50$dath$datl"
}
if { $cmd == "OR" } {
set xcode "60$dath$datl"
}
if { $cmd == "ORN" } {
set xcode "70$dath$datl"
}
if { $cmd == "ANDB" } {
set xcode "8000"
}
if { $cmd == "ORB" } {
set xcode "9000"
}
if { $cmd == "SETC" } {
set xcode "a$cname$dath$datl"
}
if { $cmd == "INC" } {
set xcode "b$cname$dath$datl"
}
if { $cmd == "DEC" } {
set xcode "c$cname$dath$datl"
}
if { $cmd == "SETT" } {
set xcode "d$cname$dath$datl"
}
if { $cmd == "COND" } {
set xcode "e$cname$dath$datl"
}
if { $cmd == "JUMP" } {
set xcode "f$dl$dath$datl"
}
return $xcode
}
ShoMemoryは、内部メモリ、入力メモリ、出力メモリを
リストボックスに表示します。
proc ShowMemory {x} {
global memory
# delete
.lstMemory delete 0 end
.lstInput delete 0 end
.lstOutput delete 0 end
for {set i 0} {$i < 31} {incr i} {
.lstMemory insert end "[format "%02d" $i] [h2b $memory($i)]"
}
# input
.lstInput insert end "0 => [h2b $memory(0)]"
.lstInput insert end "1 => [h2b $memory(1)]"
# output
.lstOutput insert end "2 => [h2b $memory(2)]"
.lstOutput insert end "3 => [h2b $memory(3)]"
}
手続きh2bは、1バイトデータを8ビットの文字列に
変換します。
proc h2b {x} {
set tmp $x
# separate
set res(0) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(1) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(2) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(3) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(4) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(5) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(6) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(7) [expr $tmp % 2]
# concatenate
set result "$res(7)$res(6)$res(5)$res(4)"
set result "$result$res(3)$res(2)$res(1)$res(0)"
#
return $result
}
入力メモリ操作
Setボタンが実現する内容は、入力メモリの値操作です。
入力メモリは、内部メモリのビットアドレス0〜15に
割当ています。
ビットアドレスから0バイト目、1バイト目を算出します。
さらに、どのビット位置かを計算します。
2つのパラメータを利用し、内部メモリのビットアドレス
を確定します。
0バイト目あるいは1バイト目の8ビットを取り出したなら
該当ビットを0あるいは1にし、書き戻します。
言葉の説明は複雑ですが、コードは簡単です。
.btnSet configure -command {
# get bit address
set adr [.sbxRange get]
# get bit value
set val [.sbxVal get]
# calculate memory index
set q [expr $adr / 8]
set r [expr $adr % 8]
# bit clear
set adr [getBitClr $memory($q) $r]
set memory($q) $adr
if { $val == "1" } {
set adr [getBitSet $memory($q) $r]
set memory($q) $adr
}
ShowMemory 0
}
スピンボックスで、ビットアドレスと値を指定してあります。
それらの値を取得して、ビットアドレスを8で割った商と余り
で、0バイトか1バイトの何ビット目かを判断します。
指定ビットの操作は、getBitClr、getBitSetの2手続き
で対応します。
ともに、1バイトの数値を8ビットに分解し、該当する
ビットを0あるいは1にします。バラバラの8ビットを
2倍する処理を繰り返し、1バイト数値に再構成します。
proc getBitSetClr {x y z} {
set tmp $x
# separate
set res(0) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(1) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(2) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(3) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(4) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(5) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(6) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(7) [expr $tmp % 2]
# judge
set res($y) $z
# concatenate
set result 0
set result [expr $result * 2 + $res(7)]
set result [expr $result * 2 + $res(6)]
set result [expr $result * 2 + $res(5)]
set result [expr $result * 2 + $res(4)]
set result [expr $result * 2 + $res(3)]
set result [expr $result * 2 + $res(2)]
set result [expr $result * 2 + $res(1)]
set result [expr $result * 2 + $res(0)]
#
return $result
}
proc getBitSet {x y} {
return [getBitSetClr $x $y 1]
}
proc getBitClr {x y} {
return [getBitSetClr $x $y 0]
}
スキャン開始位置設定
Zeroボタンが実現する内容は、スキャン開始位置
を0ラインに設定する処理です。
スキャン開始位置を指定するとともに、4つの変数を
初期化します。
- index 次に実行するライン情報を指定
- reg 命令実行結果格納
- regx 命令実行の一時値格納
- msft スタック用シフトレジスタ
初期化で、4変数の値を0にします。
.btnZero configure "Zero" -command {
set index 0
set reg 0
set regx 0
set msft 0
set pgmcode $pgm($index)
}
変数pgmcodeは、該当ラインの情報を表示する
ために利用します。
ステップ実行
Stepボタンをクリックするたびに、スキャン位置
をずらして、ニモニック情報で内部メモリの内容
を更新します。
ボタンクリックのたびに、手続きdoScanに変数
indexの値を渡し、命令に応じた処理をさせます。
処理が終わると、変数indexの値に1を加えます。
.btnStep configure "Step" -command {
doScan $index
incr index
if { $index < $last } {
set pgmcode $pgm($index)
}
if { $index == $last } {
set index 0
set reg 0
set regx 0
set msft 0
set pgmcode $pgm($index)
}
}
手続きdoScanは、変数pgmxの内容を取出して
実行するインタプリタです。
Cで記述してあるインタプリタの内容を、Tcl/Tkの
命令に置き換えたので、非常に単純です。
proc doScan {x} {
global last pgm pgmx reg regx memory mtim ctim index
if { $x < $last } {
# show memory
ShowMemory 0
# get code
set pgmxx(0) [string index $pgmx($x) 0]
set pgmxx(1) [string index $pgmx($x) 1]
set pgmxx(2) [string index $pgmx($x) 2]
set pgmxx(3) [string index $pgmx($x) 3]
set opcode [getHex $pgmxx(0)]
# get bit number
set du [getHex $pgmxx(2)]
set dl [getHex $pgmxx(3)]
set operand [expr $du * 16 + $dl]
# get memory location
set q [expr $operand / 8]
set r [expr $operand % 8]
# update memory
# END
#if { $opcode == 0 } {
#}
# LD LDN
if { $opcode == 2 || $opcode == 3 } {
pushVal $reg
set reg [getBitVal $memory($q) $r]
if { $opcode == 3 } {
set reg [getBitNot $reg]
}
}
# AND ANDN OR ORN
if { $opcode == 4 || $opcode == 5 || $opcode == 6 || $opcode == 7 } {
set regx [getBitVal $memory($q) $r]
# inverse
if { $opcode == 5 || $opcode == 7 } {
set regx [getBitNot $regx]
}
# AND ANDN
if { $opcode == 4 || $opcode == 5 } {
set reg [getBitAnd $reg $regx]
}
# OR ORN
if { $opcode == 6 || $opcode == 7 } {
set reg [getBitOr $reg $regx]
}
}
# ANDB ORB
if { $opcode == 8 || $opcode == 9 } {
set regx [pullVal 0]
if { $opcode == 8 } {
set reg [getBitAnd $reg $regx]
}
if { $opcode == 9 } {
set reg [getBitOr $reg $regx]
}
}
# OUT
if { $opcode == 1 } {
set memory($q) [getBitClr $memory($q) $r]
if { $reg == 1 } {
set memory($q) [getBitSet $memory($q) $r]
}
}
# SETC
if { $opcode == 10 } {
# get counter number
set cnumber [getHex $pgmxx(1)]
# calculate value
set cvalue [calcNumber16 [getHex $pgmxx(2)] [getHex $pgmxx(3)]]
# store
set ctim($cnumber) $cvalue
}
# INC DEC
if { $opcode == 11 || $opcode == 12 } {
# get counter number
set cnumber [getHex $pgmxx(1)]
# get value
set cvalue $ctim($cnumber)
# increment
if { $opcode == 11 } {
set cvalue [expr $cvalue + 1]
}
# decrement
if { $opcode == 12 } {
set cvalue [expr $cvalue - 1]
}
# store
set ctim($cnumber) $cvalue
}
# SETT
if { $opcode == 13 } {
# get timer number
set tnumber [getHex $pgmxx(1)]
# calculate value
set tvalue [calcNumber16 [getHex $pgmxx(2)] [getHex $pgmxx(3)]]
# store
set mtim($tnumber) $tvalue
}
# COND
if { $opcode == 14 } {
# get timer number
set tnumber [getHex $pgmxx(1)]
# get timer value
set tvalue $mtim($tnumber)
# get assist relay number
set tnumber [getHex $pgmxx(3)]
# judge
if { $tvalue == 0 } {
tk_messageBox -type ok -message "set $tnumber relay"
}
}
# JUMP
if { $opcode == 15 } {
# get line number
set nline [getHex $pgmxx(1)]
set nline [expr $nline * 16 + [getHex $pgmxx(1)]]
set nline [expr $nline + [getHex $pgmxx(2)]]
set nline [expr $nline * 16]
set nline [expr $nline + [getHex $pgmxx(3)]]
set index [expr $nline - 1]
}
# show memory
ShowMemory 0
}
}
この手続きを記述し、シミュレータを動かす
ことで、インタプリタのバグの発見と修正が
スムーズにできました。
タイマー処理
タイマーは、PLCを構成するマイコンでデクリメント
していきます。
シミュレータでは、ステップ実行中に該当するタイマーを
ゼロクリアできるカラクリを用意して対応します。
タイマー番号を指定するスピンボックスと、ゼロクリア用
ボタンを用意します。
スピンボックスには、タイマー番号が必要になるので
リスト形式で文字列を生成しておきます。
set tnumber "T000 T001 T002 T003"
set tnumber "$tnumber T004 T005 T006 T007"
set tnumber "$tnumber T100 T101 T102 T103"
set tnumber "$tnumber T104 T105 T106 T107"
スピンボックスは、オブジェクト生成で処理します。
spinbox .sbxTnumber -value $tnumber -state readonly -width 14
ゼロクリアボタンの処理は、以下と定義しました。
button .btnTzero -text "Clear timer" -command {
# get timer counter
set adr [.sbxTnumber get]
# convert timer counter
set cnt [getRegNumber $adr]
set mtim($cnt) 0
}
タイマー番号がわかれば、対応するレジスタの値を
ゼロに設定するだけです。ゼロになったときの対応
は、スキャン処理かステップ実行に任せます。
シミュレータ全ソースコード
Tcl/Tkで作成したシミュレータのソースコードを
掲載します。
#!/usr/local/bin/wish
wm title . "PLC simulator"
set theFileName ""
set fd_in ""
set last 0
for {set i 0} {$i < 32} {incr i} {
set memory($i) 0
}
set msft 0
set index 0
set pgm(0) ""
set reg 0
set regx 0
set pgmcode ""
set tnumber "T000 T001 T002 T003"
set tnumber "$tnumber T004 T005 T006 T007"
set tnumber "$tnumber T100 T101 T102 T103"
set tnumber "$tnumber T104 T105 T106 T107"
###################
# initialize timer
###################
for {set i 0} {$i < 16} {incr i} {
set mtim($i) 0
}
######################
# initialize coungter
######################
for {set i 0} {$i < 16} {incr i} {
set ctim($i) 0
}
#######################################
# define objects
#######################################
#----- label -----
label .lblFileNameLabel -text "FileName"
label .lblFileName -textvariable theFileName
label .lblMemory -text "Memory"
label .lblInput -text "Input Memory"
label .lblOutput -text "Output Memory"
label .lblRange -text "Bit Address"
label .lblVal -text "Bit Value"
label .lblIndex -text "index"
label .lblCode -text "code"
label .lblReg -text "reg"
label .lblRegx -text "regx"
label .lblStack -text "stack"
label .lblIndexV -textvariable index
label .lblCodeV -textvariable pgmcode
label .lblRegV -textvariable reg
label .lblRegxV -textvariable regx
label .lblStackV -textvariable msft
label .lblMtimV000 -textvariable mtim(0)
label .lblMtimV001 -textvariable mtim(1)
label .lblMtimV002 -textvariable mtim(2)
label .lblMtimV003 -textvariable mtim(3)
label .lblMtimV004 -textvariable mtim(4)
label .lblMtimV005 -textvariable mtim(5)
label .lblMtimV006 -textvariable mtim(6)
label .lblMtimV007 -textvariable mtim(7)
label .lblMtimV100 -textvariable mtim(8)
label .lblMtimV101 -textvariable mtim(9)
label .lblMtimV102 -textvariable mtim(10)
label .lblMtimV103 -textvariable mtim(11)
label .lblMtimV104 -textvariable mtim(12)
label .lblMtimV105 -textvariable mtim(13)
label .lblMtimV106 -textvariable mtim(14)
label .lblMtimV107 -textvariable mtim(15)
label .lblMtim000 -text "<-T000"
label .lblMtim001 -text "<-T001"
label .lblMtim002 -text "<-T002"
label .lblMtim003 -text "<-T003"
label .lblMtim004 -text "<-T004"
label .lblMtim005 -text "<-T005"
label .lblMtim006 -text "<-T006"
label .lblMtim007 -text "<-T007"
label .lblMtim100 -text "<-T100"
label .lblMtim101 -text "<-T101"
label .lblMtim102 -text "<-T102"
label .lblMtim103 -text "<-T103"
label .lblMtim104 -text "<-T104"
label .lblMtim105 -text "<-T105"
label .lblMtim106 -text "<-T106"
label .lblMtim107 -text "<-T107"
label .lblCtimV000 -textvariable ctim(0)
label .lblCtimV001 -textvariable ctim(1)
label .lblCtimV002 -textvariable ctim(2)
label .lblCtimV003 -textvariable ctim(3)
label .lblCtimV004 -textvariable ctim(4)
label .lblCtimV005 -textvariable ctim(5)
label .lblCtimV006 -textvariable ctim(6)
label .lblCtimV007 -textvariable ctim(7)
label .lblCtimV100 -textvariable ctim(8)
label .lblCtimV101 -textvariable ctim(9)
label .lblCtimV102 -textvariable ctim(10)
label .lblCtimV103 -textvariable ctim(11)
label .lblCtimV104 -textvariable ctim(12)
label .lblCtimV105 -textvariable ctim(13)
label .lblCtimV106 -textvariable ctim(14)
label .lblCtimV107 -textvariable ctim(15)
label .lblCtim000 -text "<-C000"
label .lblCtim001 -text "<-C001"
label .lblCtim002 -text "<-C002"
label .lblCtim003 -text "<-C003"
label .lblCtim004 -text "<-C004"
label .lblCtim005 -text "<-C005"
label .lblCtim006 -text "<-C006"
label .lblCtim007 -text "<-C007"
label .lblCtim100 -text "<-C100"
label .lblCtim101 -text "<-C101"
label .lblCtim102 -text "<-C102"
label .lblCtim103 -text "<-C103"
label .lblCtim104 -text "<-C104"
label .lblCtim105 -text "<-C105"
label .lblCtim106 -text "<-C106"
label .lblCtim107 -text "<-C107"
label .lblTnum -text "Timer number"
#----- list box -----
listbox .lstLadder -yscrollcommand ".scbLadderV set"
listbox .lstLCode -yscrollcommand ".scbLCodeV set"
listbox .lstMemory -yscrollcommand ".scbMemoryV set"
listbox .lstInput -yscrollcommand ".scbInputV set"
listbox .lstOutput -yscrollcommand ".scbOutputV set"
#----- scroll bar -----
scrollbar .scbLadderV -orient vertical -command ".lstLadder yview"
scrollbar .scbLCodeV -orient vertical -command ".lstLCode yview"
scrollbar .scbMemoryV -orient vertical -command ".lstMemory yview"
scrollbar .scbInputV -orient vertical -command ".lstInput yview"
scrollbar .scbOutputV -orient vertical -command ".lstOutput yview"
#----- spin box -----
spinbox .sbxRange -from 0 -to 15 -state readonly -width 4
spinbox .sbxVal -value {"0" "1"} -state readonly -width 4
spinbox .sbxTnumber -value $tnumber -state readonly -width 14
#----- button -----
button .btnExit -text "Exit" -command "exit"
button .btnLoad -text "Load" -command {
set theFileName [tk_getOpenFile -filetypes {{"text file" {.txt}}}]
# clear list box
.lstLadder delete 0 end
.lstLCode delete 0 end
# open
set fd_in [open $theFileName "r"]
# read context
set i 0
while { [gets $fd_in sbuf] >= 0 } {
# store
set pgm($i) $sbuf
# display
set tmp [format "%4d %s" $i $pgm($i)]
.lstLadder insert end $tmp
# convert
set pgmx($i) [getCode $pgm($i)]
# display
.lstLCode insert end $pgmx($i)
# increment
incr i
}
set last $i
# close
close $fd_in
ShowMemory 0
}
button .btnSet -text "Set" -command {
# get bit address
set adr [.sbxRange get]
# get bit value
set val [.sbxVal get]
# calculate memory index
set q [expr $adr / 8]
set r [expr $adr % 8]
# bit clear
set adr [getBitClr $memory($q) $r]
set memory($q) $adr
if { $val == "1" } {
set adr [getBitSet $memory($q) $r]
set memory($q) $adr
}
ShowMemory 0
}
button .btnZero -text "Zero" -command {
set index 0
set reg 0
set regx 0
set msft 0
set pgmcode $pgm($index)
}
button .btnStep -text "Step" -command {
doScan $index
incr index
if { $index < $last } {
set pgmcode $pgm($index)
}
if { $index == $last } {
set index 0
set reg 0
set regx 0
set msft 0
set pgmcode $pgm($index)
}
}
button .btnTzero -text "Clear timer" -command {
# get timer counter
set adr [.sbxTnumber get]
# convert timer counter
set cnt [getRegNumber $adr]
set mtim($cnt) 0
}
#######################################
# procedures
#######################################
proc doScan {x} {
global last pgm pgmx reg regx memory mtim ctim index
if { $x < $last } {
# show memory
ShowMemory 0
# get code
set pgmxx(0) [string index $pgmx($x) 0]
set pgmxx(1) [string index $pgmx($x) 1]
set pgmxx(2) [string index $pgmx($x) 2]
set pgmxx(3) [string index $pgmx($x) 3]
set opcode [getHex $pgmxx(0)]
# get bit number
set du [getHex $pgmxx(2)]
set dl [getHex $pgmxx(3)]
set operand [expr $du * 16 + $dl]
# get memory location
set q [expr $operand / 8]
set r [expr $operand % 8]
# update memory
# END
#if { $opcode == 0 } {
#}
# LD LDN
if { $opcode == 2 || $opcode == 3 } {
pushVal $reg
set reg [getBitVal $memory($q) $r]
if { $opcode == 3 } {
set reg [getBitNot $reg]
}
}
# AND ANDN OR ORN
if { $opcode == 4 || $opcode == 5 || $opcode == 6 || $opcode == 7 } {
set regx [getBitVal $memory($q) $r]
# inverse
if { $opcode == 5 || $opcode == 7 } {
set regx [getBitNot $regx]
}
# AND ANDN
if { $opcode == 4 || $opcode == 5 } {
set reg [getBitAnd $reg $regx]
}
# OR ORN
if { $opcode == 6 || $opcode == 7 } {
set reg [getBitOr $reg $regx]
}
}
# ANDB ORB
if { $opcode == 8 || $opcode == 9 } {
set regx [pullVal 0]
if { $opcode == 8 } {
set reg [getBitAnd $reg $regx]
}
if { $opcode == 9 } {
set reg [getBitOr $reg $regx]
}
}
# OUT
if { $opcode == 1 } {
set memory($q) [getBitClr $memory($q) $r]
if { $reg == 1 } {
set memory($q) [getBitSet $memory($q) $r]
}
}
# SETC
if { $opcode == 10 } {
# get counter number
set cnumber [getHex $pgmxx(1)]
# calculate value
set cvalue [calcNumber16 [getHex $pgmxx(2)] [getHex $pgmxx(3)]]
# store
set ctim($cnumber) $cvalue
}
# INC DEC
if { $opcode == 11 || $opcode == 12 } {
# get counter number
set cnumber [getHex $pgmxx(1)]
# get value
set cvalue $ctim($cnumber)
# increment
if { $opcode == 11 } {
set cvalue [expr $cvalue + 1]
}
# decrement
if { $opcode == 12 } {
set cvalue [expr $cvalue - 1]
}
# store
set ctim($cnumber) $cvalue
}
# SETT
if { $opcode == 13 } {
# get timer number
set tnumber [getHex $pgmxx(1)]
# calculate value
set tvalue [calcNumber16 [getHex $pgmxx(2)] [getHex $pgmxx(3)]]
# store
set mtim($tnumber) $tvalue
}
# COND
if { $opcode == 14 } {
# get timer number
set tnumber [getHex $pgmxx(1)]
# get timer value
set tvalue $mtim($tnumber)
# get assist relay number
set tnumber [getHex $pgmxx(3)]
# judge
if { $tvalue == 0 } {
tk_messageBox -type ok -message "set $tnumber relay"
}
}
# JUMP
if { $opcode == 15 } {
# get line number
set nline [getHex $pgmxx(1)]
set nline [expr $nline * 16 + [getHex $pgmxx(1)]]
set nline [expr $nline + [getHex $pgmxx(2)]]
set nline [expr $nline * 16]
set nline [expr $nline + [getHex $pgmxx(3)]]
#
set index [expr $nline - 1]
}
# show memory
ShowMemory 0
}
}
proc getCode {x} {
# get command
set cmd [lindex $x 0]
# get data
set dat [lindex $x end]
if { $cmd == $dat } {
set dath 0
set datl 0
} else {
# command SETC or SETT
if { $cmd == "SETC" || $cmd == "SETT" } {
# get counter number string
set cname [lindex $x 1]
# calculate value
set cname [format "%x" [getRegNumber $cname]]
# get counter value string
set dat [lindex $x end]
}
# command INC or DEC
if { $cmd == "INC" || $cmd == "DEC" } {
# get counter number string
set cname [lindex $x end]
# calculate value
set cname [format "%x" [getRegNumber $cname]]
# get counter value string
set dat 0
}
# command COND
if { $cmd == "COND" } {
# get counter number string
set cname [lindex $x 1]
# calculate value
set cname [format "%x" [getRegNumber $cname]]
# get relay value string
set rname [lindex $x end]
set dat [getRegNumber $rname]
}
# command JUMP
if { $cmd == "JUMP" } {
# get counter number string
set cname [lindex $x end]
# convert
set dh [expr $cname / 256]
set dl [expr $cname % 256]
# copy
set dat $dl
# separate
set dl [expr $dh % 16]
}
# convert
set dath [format "%x" [expr $dat / 16]]
set datl [format "%x" [expr $dat % 16]]
}
# conversion
if { $cmd == "END" } {
set xcode "0000"
}
if { $cmd == "OUT" } {
set xcode "10$dath$datl"
}
if { $cmd == "LD" } {
set xcode "20$dath$datl"
}
if { $cmd == "LDN" } {
set xcode "30$dath$datl"
}
if { $cmd == "AND" } {
set xcode "40$dath$datl"
}
if { $cmd == "ANDN" } {
set xcode "50$dath$datl"
}
if { $cmd == "OR" } {
set xcode "60$dath$datl"
}
if { $cmd == "ORN" } {
set xcode "70$dath$datl"
}
if { $cmd == "ANDB" } {
set xcode "8000"
}
if { $cmd == "ORB" } {
set xcode "9000"
}
if { $cmd == "SETC" } {
set xcode "a$cname$dath$datl"
}
if { $cmd == "INC" } {
set xcode "b$cname$dath$datl"
}
if { $cmd == "DEC" } {
set xcode "c$cname$dath$datl"
}
if { $cmd == "SETT" } {
set xcode "d$cname$dath$datl"
}
if { $cmd == "COND" } {
set xcode "e$cname$dath$datl"
}
if { $cmd == "JUMP" } {
set xcode "f$dl$dath$datl"
}
return $xcode
}
proc h2b {x} {
set tmp $x
# separate
set res(0) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(1) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(2) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(3) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(4) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(5) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(6) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(7) [expr $tmp % 2]
# concatenate
set result "$res(7)$res(6)$res(5)$res(4)"
set result "$result$res(3)$res(2)$res(1)$res(0)"
#
return $result
}
proc getHex {x} {
set result $x
if {[string tolower $x] == "a"} {
set result 10
}
if {[string tolower $x] == "b"} {
set result 11
}
if {[string tolower $x] == "c"} {
set result 12
}
if {[string tolower $x] == "d"} {
set result 13
}
if {[string tolower $x] == "e"} {
set result 14
}
if {[string tolower $x] == "f"} {
set result 15
}
return $result
}
proc pushVal {x} {
global msft
set xx $x
# shift
set msft [expr $msft * 2]
# store LSB
set msft [expr $msft + $xx]
}
proc pullVal {x} {
global msft
# get LSB
set result [expr $msft % 2]
# shift
set msft [expr $msft / 2]
#
return $result
}
proc getBitVal {x y} {
set tmp $x
# separate
set res(0) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(1) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(2) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(3) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(4) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(5) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(6) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(7) [expr $tmp % 2]
#
return $res($y)
}
proc getBitAnd {x y} {
set result 0
if { $x == 1 && $y == 1 } {
incr result
}
return $result
}
proc getBitOr {x y} {
set result 1
if { $x == 0 && $y == 0 } {
incr result -1
}
return $result
}
proc getBitNot {x} {
set result 0
if { $x == 0 } {
incr result
}
return $result
}
proc ShowMemory {x} {
global memory
# delete
.lstMemory delete 0 end
.lstInput delete 0 end
.lstOutput delete 0 end
for {set i 0} {$i < 31} {incr i} {
.lstMemory insert end "[format "%02d" $i] [h2b $memory($i)]"
}
# input
.lstInput insert end "0 => [h2b $memory(0)]"
.lstInput insert end "1 => [h2b $memory(1)]"
# output
.lstOutput insert end "2 => [h2b $memory(2)]"
.lstOutput insert end "3 => [h2b $memory(3)]"
}
proc getBitSetClr {x y z} {
set tmp $x
# separate
set res(0) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(1) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(2) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(3) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(4) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(5) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(6) [expr $tmp % 2] ; set tmp [expr $tmp / 2] ;
set res(7) [expr $tmp % 2]
# judge
set res($y) $z
# concatenate
set result 0
set result [expr $result * 2 + $res(7)]
set result [expr $result * 2 + $res(6)]
set result [expr $result * 2 + $res(5)]
set result [expr $result * 2 + $res(4)]
set result [expr $result * 2 + $res(3)]
set result [expr $result * 2 + $res(2)]
set result [expr $result * 2 + $res(1)]
set result [expr $result * 2 + $res(0)]
#
return $result
}
proc getBitSet {x y} {
return [getBitSetClr $x $y 1]
}
proc getBitClr {x y} {
return [getBitSetClr $x $y 0]
}
proc getRegNumber {x} {
# get number
set dh [string index $x 1]
set dl [string index $x 3]
# calculate
set result [expr $dh * 8 + $dl]
return $result
}
proc calcNumber16 {x y} {
# calculate
set result [expr $x * 16 + $y]
return $result
}
#######################################
# window area placing
#######################################
grid .lblFileNameLabel -column 0 -row 0
grid .lblFileName -column 0 -row 1
grid .lstLadder -column 0 -row 2
grid .lblMemory -column 0 -row 3
grid .lstMemory -column 0 -row 4
grid .lblIndex -column 0 -row 5
grid .lblCode -column 0 -row 6
grid .lblReg -column 0 -row 7
grid .lblRegx -column 0 -row 8
grid .lblStack -column 0 -row 9
grid .scbLadderV -column 1 -row 2 -sticky ns
grid .scbMemoryV -column 1 -row 4 -sticky ns
grid .lstLCode -column 2 -row 2
grid .lblInput -column 2 -row 3
grid .lstInput -column 2 -row 4
grid .lblIndexV -column 2 -row 5
grid .lblCodeV -column 2 -row 6
grid .lblRegV -column 2 -row 7
grid .lblRegxV -column 2 -row 8
grid .lblStackV -column 2 -row 9
grid .btnLoad -column 3 -row 0
grid .scbLCodeV -column 3 -row 2 -sticky ns
grid .scbInputV -column 3 -row 4 -sticky ns
grid .btnZero -column 3 -row 5
grid .btnStep -column 3 -row 6
grid .lblRange -column 4 -row 0
grid .sbxRange -column 4 -row 1
grid .lblOutput -column 4 -row 3
grid .lstOutput -column 4 -row 4
grid .lblVal -column 5 -row 0
grid .sbxVal -column 5 -row 1
grid .scbOutputV -column 5 -row 4 -sticky ns
grid .btnSet -column 6 -row 1
grid .btnExit -column 8 -row 21
grid .lblMtimV000 -column 4 -row 5
grid .lblMtimV001 -column 4 -row 6
grid .lblMtimV002 -column 4 -row 7
grid .lblMtimV003 -column 4 -row 8
grid .lblMtimV004 -column 4 -row 9
grid .lblMtimV005 -column 4 -row 10
grid .lblMtimV006 -column 4 -row 11
grid .lblMtimV007 -column 4 -row 12
grid .lblMtimV100 -column 4 -row 13
grid .lblMtimV101 -column 4 -row 14
grid .lblMtimV102 -column 4 -row 15
grid .lblMtimV103 -column 4 -row 16
grid .lblMtimV104 -column 4 -row 17
grid .lblMtimV105 -column 4 -row 18
grid .lblMtimV106 -column 4 -row 19
grid .lblMtimV107 -column 4 -row 20
grid .lblMtim000 -column 5 -row 5
grid .lblMtim001 -column 5 -row 6
grid .lblMtim002 -column 5 -row 7
grid .lblMtim003 -column 5 -row 8
grid .lblMtim004 -column 5 -row 9
grid .lblMtim005 -column 5 -row 10
grid .lblMtim006 -column 5 -row 11
grid .lblMtim007 -column 5 -row 12
grid .lblMtim100 -column 5 -row 13
grid .lblMtim101 -column 5 -row 14
grid .lblMtim102 -column 5 -row 15
grid .lblMtim103 -column 5 -row 16
grid .lblMtim104 -column 5 -row 17
grid .lblMtim105 -column 5 -row 18
grid .lblMtim106 -column 5 -row 19
grid .lblMtim107 -column 5 -row 20
grid .lblCtimV000 -column 6 -row 5
grid .lblCtimV001 -column 6 -row 6
grid .lblCtimV002 -column 6 -row 7
grid .lblCtimV003 -column 6 -row 8
grid .lblCtimV004 -column 6 -row 9
grid .lblCtimV005 -column 6 -row 10
grid .lblCtimV006 -column 6 -row 11
grid .lblCtimV007 -column 6 -row 12
grid .lblCtimV100 -column 6 -row 13
grid .lblCtimV101 -column 6 -row 14
grid .lblCtimV102 -column 6 -row 15
grid .lblCtimV103 -column 6 -row 16
grid .lblCtimV104 -column 6 -row 17
grid .lblCtimV105 -column 6 -row 18
grid .lblCtimV106 -column 6 -row 19
grid .lblCtimV107 -column 6 -row 20
grid .lblCtim000 -column 7 -row 5
grid .lblCtim001 -column 7 -row 6
grid .lblCtim002 -column 7 -row 7
grid .lblCtim003 -column 7 -row 8
grid .lblCtim004 -column 7 -row 9
grid .lblCtim005 -column 7 -row 10
grid .lblCtim006 -column 7 -row 11
grid .lblCtim007 -column 7 -row 12
grid .lblCtim100 -column 7 -row 13
grid .lblCtim101 -column 7 -row 14
grid .lblCtim102 -column 7 -row 15
grid .lblCtim103 -column 7 -row 16
grid .lblCtim104 -column 7 -row 17
grid .lblCtim105 -column 7 -row 18
grid .lblCtim106 -column 7 -row 19
grid .lblCtim107 -column 7 -row 20
grid .lblTnum -column 0 -row 11
grid .sbxTnumber -column 0 -row 12
grid .btnTzero -column 0 -row 13
目次
前
次