目次

シミュレータ作成

 マイコンで実現するPLCを動かす前に、シミュレータを
 利用して、動作を確認します。

 ニモニック形式になったシーケンス制御コードを読み込み
 ラダー図の1行を、逐次実行します。

 1スキャンの動作を確認します。こうすると
 16ビットの入力を任意に設定できるようにし
 入力変化があったときの動作を検証できます。

 マルチプラットホームで動作するように、Tcl/Tkで
 シミュレータを作成します。

 シミュレータ作成が、同時に1スキャン処理の
 インタプリタ動作のテストであり、デバッグに
 なります。

 シミュレータを作成することで、論理上の矛盾と
 小さなバグを修正して、Cのソースコード改修が
 できました。


画面構成

 画面構成を決めます。  シミュレーションのフローは、以下としました。
  1. テキストファイルからニモニック形式ラダー情報入力
  2. ニモニックから内部情報形式に変換
  3. 入力の初期状態指定
  4. ZEROボタンでシミュレーション開始点をスキャン先頭に設定
  5. 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ボタンを定義します。 button .btnLoad -text "Load" button .btnSet -text "Set" button .btnZero -text "Zero" button .btnStep -text "Step"  ボタンをクリックしたときの処理は、別に定義します。  内部メモリの内容を、1ビットごとに設定できるように  2つのスピンボックスを定義します。 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. テキストファイル名入力
  2. リストボックスをクリア
  3. ファイルオープン
  4. ファイルから1行リードし、変数pgmに格納
  5. 変数pgmの内容をリストボックスに追加して表示
  6. 変数pgmの内容を、内部情報に変換後、変数pgmxに保存
  7. 変数pgmxの内容をリストボックスに追加して表示
  8. EOFを検出するまで、上記4処理を繰り返す
  9. ファイルクローズ
 フローから、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つの変数を  初期化します。  初期化で、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
目次

inserted by FC2 system