diff options
Diffstat (limited to 'lib/rightpanel/regwatches.tcl')
-rwxr-xr-x | lib/rightpanel/regwatches.tcl | 2120 |
1 files changed, 2120 insertions, 0 deletions
diff --git a/lib/rightpanel/regwatches.tcl b/lib/rightpanel/regwatches.tcl new file mode 100755 index 0000000..ad5e433 --- /dev/null +++ b/lib/rightpanel/regwatches.tcl @@ -0,0 +1,2120 @@ +#!/usr/bin/tclsh +# Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) + +############################################################################ +# Copyright (C) 2007-2009 by Martin Ošmera # +# martin.osmera@gmail.com # +# # +# This program is free software; you can redistribute it and#or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +############################################################################ + +# -------------------------------------------------------------------------- +# DESCRIPTION +# Implements register watches (part of Right Panel) +# -------------------------------------------------------------------------- + +class RegWatches { + + ## COMMON + common watches_set_shortcuts {} ;# Currently set shortcuts for register watches + common watches_shortcuts_cat {watches} ;# Key shortcut categories related to register watches + # Counter of embedded entry widgets in register watches + common watch_entry_count 0 + # Conter of instances + common count 0 + ## Highlighting tags for register watches + # { + # {tag_name foreground_color ?bold_or_italic?} + # ... + # } + common watch_text_tags { + {tag_Baddr #DD0000 1} + {tag_Xaddr #AA00FF 1} + {tag_Eaddr #00AAFF 1} + {tag_addr #0000DD 1} + {tag_name #8888DD {}} + } + common regfont [font create -family $::DEFAULT_FIXED_FONT -size -14] + # Popup menu for register watches + common WATCHMENU { + {command {Move top} {$watches:top} 0 "rightPanel_watch_move_top" + {top} "Move this register watch to the top of list"} + {command {Move up} {$watches:up} 0 "rightPanel_watch_move_up" + {1uparrow} "Move this register watch up"} + {command {Move down} {$watches:down} 1 "rightPanel_watch_move_down" + {1downarrow} "Move this register watch down"} + {command {Move bottom} {$watches:bottom} 2 "rightPanel_watch_move_bottom" + {bottom} "Move this register watch to the bottom of list"} + {separator} + {command {Remove} {$watches:remove} 2 "rightPanel_watch_remove" + {button_cancel} "Remove this register watch from the list"} + {separator} + {command {Remove all} {$watches:remove_all} 0 "rightPanel_watch_clear" + {editdelete} "Clear the list of register watches"} + {separator} + {command {Save} {} 0 "rightPanel_watch_save {} 1" {filesave} + "Save this list to a file"} + {command {Configure} {} 0 "rightPanel_configure 0" {configure} + "Configure this panel"} + } + # Configuration menu + common CONFMENU { + {cascade {Sort by} 0 "" .sort false 1 { + {command {Name} {} 0 "rightPanel_watch_sort_by N" {} + ""} + {command {Address} {} 0 "rightPanel_watch_sort_by A" {} + ""} + {command {Type} {} 0 "rightPanel_watch_sort_by T" {} + ""} + {separator} + {radiobutton "Incremental" {} ::RegWatches::sorting_order 1 + {} 0 ""} + {radiobutton "Decremental" {} ::RegWatches::sorting_order 0 + {} 0 ""} + }} + {command {Remove all} {} 0 "rightPanel_watch_clear" {editdelete} + ""} + {separator} + {checkbutton {Autoload from code listing} {} {::RegWatches::menu_autoload} 1 0 0 + {rightPanel_watch_toggle_autoload_flag}} + } + + ## PRIVATE + private variable enabled 0 ;# Bool: enable procedures which are needless while loading project + private variable obj_idx ;# Number of this object + private variable parent ;# Widget: parent widget + private variable gui_initialized 0 ;# Bool: GUI initialized + + private variable conf_button ;# Widget: Configuration button + private variable conf_menu {} ;# Configuration menu + private variable watch_menu ;# ID of popup menu for "Register watches" + private variable watch_text ;# ID of text widget representing list of register watches + private variable watch_remove_button ;# ID of button "Remove watch" - Register watches + private variable watch_new_button ;# ID of button "New watch" - Register watches + private variable watch_add_button ;# ID of button "Add watch" - Register watches + private variable watch_addr_entry ;# ID of entry "Address" - Register watches + private variable watch_search_entry ;# ID of entry "Search" - Register watches + private variable watch_search_clear ;# ID of button "Clear search entry" - Register watches + + # Bool: Autoload from LST file + private variable autoload_flag [lindex $::CONFIG(REGWATCHES_CONFIG) 0] + private variable watches_modified 0 ;# Bool: Register watches definition modified + private variable search_val_in_progress 0 ;# Bool: Search entry validation in porgress + private variable watch_file_name {} ;# Name of file currently loaded in register watches + private variable watch_curLine 0 ;# Current line in list of register watches + private variable watch_AN_valid_ena 1 ;# Bool: Enable validation of address entry in register watches + private variable validator_engaged 0 ;# Bool: Address entry validation in progress + private variable watches_enabled 0 ;# Bool: Entry widgets in register watches enabled + + private variable watch_addrs {} ;# List - {hex_addr hex_addr ...} + ## Array of Lists - info about particular watch + # format: $watch_data($hex_addr) -> {regName textVariable} + # note: embedded entry path == $watch_text.$textVariable + private variable watch_data + + + ## Object constructor + constructor {} { + incr count + set obj_idx $count + } + + ## Object destructor + destructor { + if {$gui_initialized} { + # Remove status bar tips for popup menus + menu_Sbar_remove $watch_menu + + # Unallocate GUI related variables + unset RightPanel::watch_addr$obj_idx + unset RightPanel::watch_name$obj_idx + } + } + + ## Prepare object for creating its GUI + # @parm Widget _parent - GUI parent widget + # @parm String watches_file - Definition file for register watches + # @return void + public method PrepareRegWatches {_parent filename} { + set parent $_parent + set watch_file_name $filename + set gui_initialized 0 + } + + ## Create GUI of register watches + # @return void + public method CreateRegWatchesGUI {} { + if {$gui_initialized} {return} + set gui_initialized 1 + + # Top frame + set icon_bar [frame $parent.frm_rightPanel_watch_iconBar] + # Bottom frame + set text_frame [frame $parent.frm_rightPanel_watch_txt] + # Toolbar + set tool_bar [frame $parent.frm_rightPanel_watch_toolBar] + + # Button "Configure" + set button [ttk::button $icon_bar.conf_but \ + -image ::ICONS::16::configure \ + -command "$this rightPanel_watch_cfg_menu" \ + -style Flat.TButton \ + ] + set conf_button $button + pack $button -side left -padx 3 + # Separator + pack [ttk::separator $icon_bar.sep_rightPanel_watch_ib_sepm0 \ + -orient vertical \ + ] -side left -fill y -padx 3 + # Button "Save" + set button [ttk::button $icon_bar.but_rightPanel_watch_save \ + -image ::ICONS::16::filesave \ + -command "$this rightPanel_watch_save {} 1" \ + -style Flat.TButton \ + ] + pack $button -side left -padx 3 + DynamicHelp::add $button -text [mc "Save"] + setStatusTip -widget $button \ + -text [mc "Save"] + # Button "Save as" + set button [ttk::button $icon_bar.but_rightPanel_watch_saveas \ + -image ::ICONS::16::filesaveas \ + -command "$this rightPanel_watch_saveas" \ + -style Flat.TButton \ + ] + pack $button -side left -padx 3 + DynamicHelp::add $button -text [mc "Save under a different filename"] + setStatusTip -widget $button \ + -text [mc "Save under a different filename"] + # Separator + pack [ttk::separator $icon_bar.sep_rightPanel_watch_ib_sep0 \ + -orient vertical \ + ] -side left -fill y -padx 3 + # Button "Open" + set button [ttk::button $icon_bar.but_rightPanel_watch_open \ + -image ::ICONS::16::fileopen \ + -command "$this rightPanel_watch_open" \ + -style Flat.TButton \ + ] + pack $button -side left -padx 3 + DynamicHelp::add $button -text [mc "Open *.wtc file"] + setStatusTip -widget $button \ + -text [mc "Open *.wtc file"] + # Button "Import" + set button [ttk::button $icon_bar.but_rightPanel_watch_imp \ + -image ::ICONS::16::fileimport \ + -style Flat.TButton \ + -command "$this rightPanel_watch_import" \ + ] + pack $button -side left -padx 3 + DynamicHelp::add $button -text [mc "Import list of registers from code listing or WTC file"] + setStatusTip -widget $button \ + -text [mc "Import list of registers from *.lst or *.wtc file"] + # Entry "Search" + set watch_search_entry [ttk::entry $icon_bar.search_entry \ + -validate key \ + -width 0 \ + -validatecommand "$this rightPanel_watch_search_validate %P" \ + ] + DynamicHelp::add $watch_search_entry \ + -text [mc "Enter your search string here"] + pack $watch_search_entry -side left -fill x -expand 1 + setStatusTip -widget $watch_search_entry \ + -text [mc "Search for a name"] + # Button "Clear search string" + set watch_search_clear [ttk::button $icon_bar.clear_search \ + -image ::ICONS::16::clear_left \ + -command "$watch_search_entry delete 0 end" \ + -state disabled \ + -style Flat.TButton \ + ] + DynamicHelp::add $icon_bar.clear_search -text [mc "Clear search string"] + pack $watch_search_clear -side right -after $watch_search_entry + setStatusTip -widget $icon_bar.clear_search \ + -text [mc "Clear search string"] + + # Entry "Address" + set entry [ttk::entry $tool_bar.ent_rightPanel_watch_addr \ + -textvariable RightPanel::watch_addr$count \ + -validatecommand "$this rightPanel_watch_addr_validate %P" \ + -validate key \ + -width 4 \ + ] + DynamicHelp::add $entry \ + -text [mc "Register address:\n 1 or 2 digits\tinternal RAM (not SFR)\n 3 digits\t\texpanded RAM\n 4 digits\t\texternal RAM\n dot and 2 digits\tBit"] + setStatusTip -widget $entry \ + -text [mc "Register address or bit address"] + grid $entry -sticky w -row 2 -column 1 + bind $entry <Return> "$this rightPanel_watch_add" + bind $entry <KP_Enter> "$this rightPanel_watch_add" + + # Entry "Name" + set entry [ttk::entry $tool_bar.ent_rightPanel_watch_name \ + -textvariable RightPanel::watch_name$count \ + -validatecommand "$this rightPanel_watch_name_validate %P" \ + -validate key \ + -width 20 \ + ] + setStatusTip -widget $entry \ + -text [mc "Name of the watch. Any string."] + grid $entry -sticky w -row 2 -column 2 -padx 3 + bind $entry <Return> "$this rightPanel_watch_add" + bind $entry <KP_Enter> "$this rightPanel_watch_add" + # Button "Add" + set watch_add_button [ttk::button $tool_bar.but_rightPanel_watch_add \ + -image ::ICONS::16::add \ + -command "$this rightPanel_watch_add" \ + -style Flat.TButton \ + ] + DynamicHelp::add $watch_add_button -text [mc "Add this entry to register watches"] + setStatusTip -widget $watch_add_button \ + -text [mc "Add this entry to register watches"] + grid $watch_add_button -sticky w -row 2 -column 3 + # Button "New" + set watch_new_button [ttk::button $tool_bar.but_rightPanel_watch_new \ + -image ::ICONS::16::filenew \ + -command "$this rightPanel_watch_new" \ + -style Flat.TButton \ + ] + DynamicHelp::add $watch_new_button -text [mc "New register watches entry"] + setStatusTip -widget $watch_new_button \ + -text [mc "Create new register watch"] + grid $watch_new_button -sticky w -row 2 -column 4 + # Button "Remove" + set watch_remove_button [ttk::button $tool_bar.but_rightPanel_watch_remove \ + -image ::ICONS::16::button_cancel \ + -command "$this rightPanel_watch_remove" \ + -style Flat.TButton \ + ] + DynamicHelp::add $watch_remove_button -text [mc "Remove this entry"] + setStatusTip -widget $watch_remove_button \ + -text [mc "Remove this entry"] + grid $watch_remove_button -sticky w -row 2 -column 5 + # Label "Addr" + set watch_addr_entry [label $tool_bar.lbl_rightPanel_watch_addr \ + -text [mc "Addr"] \ + -font ${Simulator_GUI::smallfont} \ + -fg ${Simulator_GUI::small_color} \ + ] + grid $watch_addr_entry -row 1 -column 1 + # Label "Register name" + grid [label $tool_bar.lbl_rightPanel_watch_name \ + -text [mc "Register name"] \ + -font ${Simulator_GUI::smallfont} \ + -fg ${Simulator_GUI::small_color} \ + ] -row 1 -column 2 + + # Create text widget representing list of register watches + set watch_text [text $text_frame.txt_rightPanel_watch \ + -yscrollcommand "$text_frame.src_rightPanel_watch set" \ + -bg {#FFFFFF} -font $regfont \ + -cursor left_ptr \ + -state disabled -exportselection 0 \ + ] + # Create text tags + $this right_panel_create_highlighting_tags $watch_text $watch_text_tags -1 + $watch_text tag configure tag_curLine -background ${::RightPanel::selection_color_dark} + + # Create scrollbar + pack [ttk::scrollbar $text_frame.src_rightPanel_watch \ + -orient vertical \ + -command "$watch_text yview" \ + ] -side right -fill y + pack $watch_text -side left -fill both -expand 1 + # Create event bindings + bind $watch_text <ButtonRelease-3> "$this rightPanel_watch_popupmenu %X %Y %x %y; break" + bind $watch_text <Button-1> "$this rightPanel_watch_click %x %y; break" + bind $watch_text <<Selection>> "false_selection $watch_text; break" + bind $watch_text <Key-Menu> {break} + + # Pack frames + pack $tool_bar -side bottom -anchor w + pack $icon_bar -side top -fill x + pack $text_frame -side top -fill both -expand 1 + rightPanel_watch_switch_line 1 + + # Create popup menu + set watch_menu $parent.menu_rightPanel_watch + regwatches_makePopupMenu + + # Refresh highlighting tags + rightPanel_refresh_regwatches_highlighting + + # Open definition file + if {$watch_file_name != {}} { + rightPanel_watch_openfile $watch_file_name . 0 + } + } + + ## Sort register watches + # @parm Char sorting_key + # N - Sort by name + # A - Sort by address + # T - Sort by type + # @return void + public method rightPanel_watch_sort_by {sorting_key} { + # Pack all watches into one sigle list + set list_to_sort [list] + set type {} + foreach addr [array names watch_data] { + + switch -- [string length $addr] { + 4 { + set type {X} + } + 3 { + if {[string index $addr 0] == {.}} { + set type {B} + } { + set type {E} + } + } + default { + set type {D} + } + } + + lappend list_to_sort [list $addr [lindex $watch_data($addr) 0] $type] + } + + # Incremental sorting order + if {$::RegWatches::sorting_order} { + set order {-increasing} + # Decremental sorting order + } { + set order {-decreasing} + } + + switch -- $sorting_key { + N { ;# Name + set index 1 + } + A { ;# Address + set index 0 + } + T { ;# Type + set index 2 + } + } + + # Sort the list + set list_to_sort [lsort -dictionary $order -index $index $list_to_sort] + + # Refill the panel + rightPanel_watch_clear 1 + foreach entry $list_to_sort { + set addr [lindex $entry 0] + set name [lindex $entry 1] + + rightPanel_watch_add $addr $name + } + } + + ## Perform autoload on simulator start + # @parm String filename - Code listinf file + # @return void + public method rightPanel_watch_autoload {filename} { + if {!$autoload_flag} {return} + if {![file exists $filename]} {return} + + rightPanel_watch_import_file $filename . + } + + ## Autoload flag toggled (this function should be invoked from configuration menu) + # @return void + public method rightPanel_watch_toggle_autoload_flag {} { + set autoload_flag $::RegWatches::menu_autoload + } + + ## Get configuration list + # @return void + public method rightPanel_watch_get_config {} { + return [list $::RegWatches::menu_autoload $::RegWatches::sorting_order] + } + + ## Create configuration menu + # @return void + private method create_conf_menu {} { + if {$conf_menu != {}} { + return + } + set conf_menu $parent.conf_menu + menuFactory $CONFMENU $conf_menu 0 "$this " 0 {} + + watch_disEna_buttons + } + + ## Invoke configuration menu + # @return void + public method rightPanel_watch_cfg_menu {} { + create_conf_menu + + set x [winfo rootx $conf_button] + set y [winfo rooty $conf_button] + incr y [winfo height $conf_button] + + set ::RegWatches::menu_autoload $autoload_flag + tk_popup $conf_menu $x $y + } + + ## Refresh highlighting tags + # @return void + public method rightPanel_refresh_regwatches_highlighting {} { + if {!$gui_initialized} {return} + $this right_panel_create_highlighting_tags $watch_text $watch_text_tags -1 + } + + ## Recreate popup menu + # @return void + public method regwatches_makePopupMenu {} { + if {!$gui_initialized} {return} + if {[winfo exists $watch_menu]} {destroy $watch_menu} + menuFactory $WATCHMENU $watch_menu 0 "$this " 0 {} + } + + + ## Retrun name of file currently loaded in register watches + # @return String - filename + public method getWatchesFileName {} { + # Determinate project root path + set prj_path [$this cget -projectPath] + append prj_path {/} + + # Return relative directory location + if {![string first $prj_path $watch_file_name]} { + return [string range $watch_file_name [string length $prj_path] end] + # Return absolute directory location + } { + return $watch_file_name + } + } + + ## (Re)set dynamic shortcuts for the given entry widget + # @parm Widget entry - Target entry widget + # @return void + private method watch_entry_shortcuts_reset {entry} { + if {!$gui_initialized} {return} + + # Unset previous configuration + foreach key $watches_set_shortcuts { + bind $entry <$key> {} + } + set set_shortcuts {} + + # Iterate over shortcuts definition + foreach block ${::SHORTCUTS_LIST} { + # Determinate category + set category [lindex $block 0] + if {[lsearch $watches_shortcuts_cat $category] == -1} {continue} + + # Determinate definition list and its length + set block [lreplace $block 0 2] + set len [llength $block] + + # Iterate over definition list and create bindings + for {set i 0; set j 1} {$i < $len} {incr i 2; incr j 2} { + # Determinate key sequence + set key [lindex $block $i] + if {[catch { + set key $::SHORTCUTS_DB($category:$key) + }]} then { + continue + } + if {$key == {}} {continue} + + # Create and register new binding + lappend watches_set_shortcuts $key + set cmd [subst [lindex $block [list $j 1]]] + append cmd {;break} + bind $entry <$key> $cmd + } + } + } + + ## Get adjusted value of address entry (register watches) + # @parm String string - input data ("\n" == get value from entry widget) + # @return String - result address + private method get_watchAddr {string} { + + # Evaluate input string + if {$string == {} || $string == {.}} { + return { .00} + } + if {$string == "\n"} { + set string [subst "\$::RightPanel::watch_addr${obj_idx}"] + regsub {^\s+} $string {} string + } + if {[string index $string 0] == {.}} { + set string [string replace $string 0 0] + set bit_addr 1 + } { + set bit_addr 0 + } + + # Adjust address + set len [string length $string] + if {$len > 2} { + if {$len != 4} { + set string " $string" + } + } { + if {$len == 1} { + set string "0$string" + } + set string " $string" + } + + # Resturn result + if {$bit_addr} { + return [string replace $string 1 1 {.}] + } { + return [string toupper $string] + } + } + + ## Get adjusted value of register name (Register watches) + # @parm String string - input data ("\n" == get value from entry widget) + # @return String - resulting register name + private method get_watchName {string} { + # Conditionaly get value from entry widget + if {$string == "\n"} { + set string [subst "\$::RightPanel::watch_name${obj_idx}"] + regsub {\t+$} $string {} string + } + + # Adjust resulting string + set len [string length $string] + append string [string repeat { } [expr {23 - $len}]] + + # Return result + return $string + } + + ## Validate content of address entry - Register watches + # @parm String value - input value + # @return Bool - result + public method rightPanel_watch_addr_validate {value} { + + # Check if validation is enabled + if {!$watch_AN_valid_ena} {return 1} + + # Check for allowed length + if {[string length $value] > 4} { + return 0 + } + + # Check for allowed characters + if {!([regexp {^[A-Fa-f0-9]*$} $value] || [regexp {^\.[A-Fa-f0-9]{0,2}$} $value])} { + return 0 + } + + # Change content of address field in register watches text widget + if {$watch_curLine != 0 && $value != {}} { + + # Get address + set value [get_watchAddr $value] + regsub {^\s+} $value {} real_value + + # Check it the desired address is unique + if {[lsearch -ascii -exact $watch_addrs $real_value] != -1} { + Sbar [mc "Unable to assign, address is already in use"] + return 1 + } + + # Modify variables related to the current entry + set idx [expr {$watch_curLine - 1}] + set addr [lindex $watch_addrs $idx] + set var [lindex $watch_data($addr) 1] + set watch_data($real_value) $watch_data($addr) + unset watch_data($addr) + lset watch_addrs $idx $real_value + + # Synchronize + $watch_text.$var configure -state normal + rightPanel_watch_sync $real_value + $watch_text.$var configure -fg ${Simulator::normal_color} + if {!$watches_enabled || [read_from_simulator $real_value] == {--}} { + $watch_text.$var configure -state disabled + } + + # Enable entry watches text widget + $watch_text configure -state normal + + # Change content of address field + $watch_text delete $watch_curLine.0 $watch_curLine.4 + $watch_text insert $watch_curLine.0 $value + + # Bit -> Byte + if {[string index $addr 0] == {.} && [string index $real_value 0] != {.}} { + $watch_text.$var configure -width 2 + help_window_hide + + # Byte -> Bit + } elseif {[string index $addr 0] != {.} && [string index $real_value 0] == {.}} { + $watch_text.$var configure -width 1 + help_window_hide + } + + # Highlight address field + set len [string length $real_value] + if {$len == 4} { + set addr_tag {tag_Xaddr} + } elseif {$len == 3 && ([string index $real_value 0] == {.})} { + set addr_tag {tag_Baddr} + } elseif {$len == 3} { + set addr_tag {tag_Eaddr} + } else { + set addr_tag {tag_addr} + } + $watch_text tag add $addr_tag $watch_curLine.0 $watch_curLine.4 + + # Change selection + $watch_text tag remove tag_curLine 1.0 end + $watch_text tag add tag_curLine $watch_curLine.0 "$watch_curLine.0 + 1 line" + + $watch_text configure -state disabled + + # Adjust help window + help_window_update_addr $addr $real_value + bind $watch_text.[lindex $watch_data($real_value) 1] <Enter> \ + "$this create_help_window_ram ${real_value}h; help_window_variable_addr" + + # Adjust flag: modified + set watches_modified 1 + } + + # Success + return 1 + } + + ## Validate content of register name entry - Register watches + # @parm String value - input value + # @return Bool - result + public method rightPanel_watch_name_validate {value} { + # Check if validation is enabled + if {!$watch_AN_valid_ena} {return 1} + + # Check for allowed length + if {[string length $value] > 23} { + return 0 + } + + # Change content of register name field in register watches text widget + if {$watch_curLine != 0} { + # Local variables + set name [get_watchName $value] ;# Register name + + # Change reg. name in object variable watch_data + lset watch_data([lindex $watch_addrs [expr {$watch_curLine - 1}]]) 0 [string trimright $value] + + # Enable list of watches widget + $watch_text configure -state normal + + # Change current register name + $watch_text delete $watch_curLine.5 "$watch_curLine.0 lineend - 1 char" + $watch_text insert $watch_curLine.5 $name + + # Restore reg. name text tag + $watch_text tag add tag_name $watch_curLine.5 $watch_curLine.28 + + # Adjust selection + $watch_text tag remove tag_curLine 1.0 end + $watch_text tag add tag_curLine $watch_curLine.0 "$watch_curLine.0 + 1 line" + + # Disable list of watches widget + $watch_text configure -state disabled + + # Adjust status modified + set watches_modified 1 + } + + # Success + $watch_search_entry delete 0 end + return 1 + } + + ## Validate content of embedded entries in list of register watches + # @parm String addr - hexadecimal representation of register addres + # @parm String value - string to validate + # @return Bool - result + public method rightPanel_watch_value_validate {addr value} { + if {$validator_engaged} {return 1} + set validator_engaged 1 + + # Check for allowed length + if {[string length $value] > 2} { + set validator_engaged 0 + return 0 + } + + # Check for allowed characters + if {![regexp {^[A-Fa-f0-9]*$} $value]} { + set validator_engaged 0 + return 0 + } + + ## Synchronize new content with simulator engine + if {[string index $addr 0] == {.}} { + set addr [string replace $addr 0 0] + set bit_addr 1 + } { + set bit_addr 0 + } + set dec_addr [expr "0x$addr"] + # Bit + if {$bit_addr} { + if {$value == {}} { + set value 0 + } + if {![regexp {^[01]?$} $value]} { + set validator_engaged 0 + return 0 + } + $this setBit $dec_addr $value + + # External RAM + } elseif {[string length $addr] == 4} { + $this setXdata $dec_addr $value + $this Simulator_XDATA_sync $addr + + # Expanded RAM + } elseif {[string length $addr] == 3} { + $this setEram $dec_addr $value + $this Simulator_XDATA_sync $addr + + # Internal RAM + } else { + $this setData $dec_addr $value + $this SimGUI_disable_sync + $this Simulator_GUI_sync I $dec_addr + $this SimGUI_enable_sync + } + + if {$bit_addr} { + set addr .$addr + } + $watch_text.[lindex $watch_data($addr) 1] configure -fg ${Simulator::normal_color} + + # Synchronize with help window + help_window_update $addr $value + + # Done ... + set validator_engaged 0 + return 1 + } + + ## Enable entry widgets in register watches + # AFFECT ALL ENTRIES (not only valid ones) !!! + # @return void + public method rightPanel_watch_force_enable {} { + if {!$gui_initialized} {CreateRegWatchesGUI} + + set watches_enabled 1 + foreach addr $watch_addrs { + $watch_text.[lindex $watch_data($addr) 1] configure -state normal + } + } + + ## Enable entry widgets in register watches + # Affect only entries with valid address (implemented on current MCU) + # @return void + public method rightPanel_watch_enable {} { + if {!$gui_initialized} {CreateRegWatchesGUI} + + set watches_enabled 1 + foreach addr $watch_addrs { + if {[string index $addr 0] == {.}} { + set addr [string replace $addr 0 0] + set bit_addr 1 + } { + set bit_addr 0 + } + set dec_addr [expr "0x$addr"] + set len [string length $addr] + + # Bit + if {$bit_addr} { + if {$dec_addr > 0x7F} { + if {![$this simulator_is_sfr_avaliable [$this getRegOfBit $dec_addr]]} { + continue + } + } + set addr ".$addr" + + # Internal RAM + } elseif {$len < 3} { + if {$dec_addr >= [lindex [$this cget -procData] 3]} { + continue + } + + # Expanded RAM + } elseif {$len == 3} { + if {$dec_addr >= [lindex [$this cget -procData] 8]} { + continue + } + + # External RAM + } else { + if {$dec_addr >= [$this cget -P_option_mcu_xdata]} { + continue + } + } + + $watch_text.[lindex $watch_data($addr) 1] configure -state normal + } + } + + ## Disable all entry widgets in register watches + # @return void + public method rightPanel_watch_disable {} { + if {!$gui_initialized} {return} + + set watches_enabled 0 + foreach addr $watch_addrs { + $watch_text.[lindex $watch_data($addr) 1] configure -state disabled + } + } + ## Select line in list of register watches + # @parm Int x - relative X coordinate + # @parm Int y - relative Y coordinate + # @return void + public method rightPanel_watch_click {x y} { + rightPanel_watch_switch_line [expr {int([$watch_text index @$x,$y])}] + $watch_search_entry delete 0 end + } + + ## Change current line in register watches + # @parm Int row - target line + # @return void + public method rightPanel_watch_switch_line {row} { + if {!$gui_initialized} {return} + set watch_AN_valid_ena 0 + + # Determinate number of the last row + set end [$watch_text index end] + set end [expr {int($end) - 1}] + + # Restore previous state of the last selected entry box + if {$watch_curLine} { + set addr [lindex $watch_addrs [expr {$watch_curLine - 1}]] + set var [lindex $watch_data($addr) 1] + $watch_text.$var selection clear + $watch_text.$var configure \ + -disabledbackground {#FFFFFF} \ + -bg {#FFFFFF} + } + + # Enable/Disable buttons and Clear/Keep entry widgets at the bottom + if {$row == $end} { + $watch_remove_button configure -state disabled + $watch_new_button configure -state disabled + $watch_add_button configure -state normal + set watch_curLine 0 + $watch_text tag remove tag_curLine 1.0 end + set ::RightPanel::watch_name${obj_idx} {} + set ::RightPanel::watch_addr${obj_idx} {} + set watch_AN_valid_ena 1 + return 0 + } { + set watch_curLine $row + $watch_remove_button configure -state normal + $watch_new_button configure -state normal + $watch_add_button configure -state disabled + } + + # Determinate text indexes + set idx0 "$row.0" + set idx1 [expr {$row + 1}] + append idx1 {.0} + + # Set selection tag + $watch_text tag remove tag_curLine 1.0 end + $watch_text tag add tag_curLine $idx0 $idx1 + + # Adjust content of entry widgets at the bottom + set addr [lindex $watch_addrs [expr {$watch_curLine - 1}]] + set name [lindex $watch_data($addr) 0] + set var [lindex $watch_data($addr) 1] + + set ::RightPanel::watch_name${obj_idx} [string trimright $name] + set ::RightPanel::watch_addr${obj_idx} [string trimright $addr] + + # Change foreground color of current value entry + $watch_text.$var configure \ + -fg ${Simulator::normal_color} \ + -bg ${::RightPanel::selection_color_dark} \ + -disabledbackground ${::RightPanel::selection_color_dark} + + # Focus on value entry + focus $watch_text.$var + $watch_text.$var selection range 0 end + $watch_text see $row.0 + + set watch_AN_valid_ena 1 + } + + ## Create a new register watch + # @parm String - Hex address, {} == Content of address entry + # @parm String - Register name, {} == Content of name entry + # @return void + public method rightPanel_watch_add args { + # Local variables + set row [$watch_text index end] ;# Last row in the list + set row [expr {int($row) - 1}] + set addr [lindex $args 0] ;# Register address (Hex String) + set name [lindex $args 1] ;# Watch name + set shortAddr {} ;# Register address (Hex Number) + + if {$addr == {} || $name == {}} { + set addr [get_watchAddr "\n"] + set name [get_watchName "\n"] + set no_sbar 0 + } { + set addr [get_watchAddr $addr] + set name [get_watchName $name] + set no_sbar 1 + } + set shortAddr [regsub {^\s*} $addr {}] + + # Check address validity + if {$shortAddr == {}} { + if {!$no_sbar} { + Sbar [mc "You must specify the register address."] + } + return 0 + } + if {[lsearch -ascii -exact $watch_addrs $shortAddr] != -1} { + if {!$no_sbar} { + Sbar [mc "Specified address is already used."] + } + return 0 + } + + # Enable text widget + $watch_text configure -state normal + # Insert address and watch name + $watch_text insert end "$addr $name" + # Insert text tags + regsub {^ +} $addr {} addr + set entry [watch_create_entry $addr $row $watch_entry_count] + $watch_text window create end -window $entry -pady 0 + $watch_text insert end "\n" + set len [string length $addr] + if {$len == 4} { + set addr_tag {tag_Xaddr} + } elseif {$len == 3 && ([string index $addr 0] == {.})} { + set addr_tag {tag_Baddr} + } elseif {$len == 3} { + set addr_tag {tag_Eaddr} + } else { + set addr_tag {tag_addr} + } + $watch_text tag add $addr_tag $row.0 $row.4 + $watch_text tag add tag_name $row.5 $row.28 + # Disable text widget + $watch_text configure -state disabled + + # Register new watch + regsub {\t+$} $name {} name + lappend watch_addrs $addr + set watch_data($addr) [list $name $watch_entry_count] + + # Synchronize + rightPanel_watch_sync $addr + $entry configure -fg ${Simulator::normal_color} + + # Enable/Disable the entry widget + if {!$watches_enabled} { + $entry configure -state disabled + } + + incr watch_entry_count + + # Reevaluate button states + watch_disEna_buttons + + # Clear search entry + $watch_search_entry delete 0 end + + # Adjust status modified + set watches_modified 1 + } + + ## Create entry widget for embedding in list of watches + # @parm String addr - hexadecimal register address + # @parm Int row - target row in text widget + # @parm Variable var - entry text variable + # @return Widget - resulting entry widget + private method watch_create_entry {addr row var} { + if {[string index $addr 0] == {.}} { + set width 1 + } { + set width 2 + } + + # Create entry widget + set entry [entry $watch_text.$var \ + -width $width \ + -font ${::Simulator_GUI::entry_font} \ + -bg {#FFFFFF} -validate key \ + -takefocus 0 -highlightthickness 0 \ + -disabledbackground {#FFFFFF} \ + -vcmd "$this rightPanel_watch_value_validate $addr %P" \ + -bd 0 -justify right \ + ] + + # Set event bindings + bind $entry <Button-1> "$this rightPanel_watch_switch_line $row" + bind $entry <Key-Up> "$this rightPanel_watch_up 1" + bind $entry <Key-Down> "$this rightPanel_watch_down 1" + bind $entry <Key-Next> "$this rightPanel_watch_down 4" + bind $entry <Key-Prior> "$this rightPanel_watch_up 4" + bind $entry <Motion> {help_window_show %X %Y} + bind $entry <Leave> {help_window_hide} + bind $entry <Enter> "$this create_help_window_ram ${addr}h; help_window_variable_addr" + bind $entry <Button-4> "$watch_text yview scroll -5 units" + bind $entry <Button-5> "$watch_text yview scroll +5 units" + watch_entry_shortcuts_reset $entry + + # Return entry reference + return $entry + } + + ## Clear highlight for all registers + # @return void + public method rightPanel_watch_clear_highlight {} { + if {!$gui_initialized} {return} + + foreach addr $watch_addrs { + $watch_text.[lindex $watch_data($addr) 1] configure -fg ${Simulator::normal_color} + } + } + + ## Clear highlight for the given register + # @return void + public method rightPanel_watch_unhighlight {addr} { + if {!$gui_initialized} {return} + + if {[lsearch $watch_addrs $addr] == -1} { + return + } + $watch_text.[lindex $watch_data($addr) 1] configure -fg ${Simulator::normal_color} + } + + ## Move current watch to the top + # @return void + public method rightPanel_watch_move_top {} { + rightPanel_watch_move 1 + } + + ## Move current watch to up + # @return void + public method rightPanel_watch_move_up {} { + # Determinate target line + set target_line [expr {$watch_curLine - 1}] + if {$target_line == 0} {return 0} + # Move watch + rightPanel_watch_move $target_line + } + + ## Move current watch to down + # @return void + public method rightPanel_watch_move_down {} { + # Determinate target line + set target_line [expr {$watch_curLine + 1}] + set end [$watch_text index end] + set end [expr {int($end) - 1}] + if {$target_line == $end} {return 0} + # Move watch + rightPanel_watch_move $target_line + } + + ## Move current watch to the bottom + # @return void + public method rightPanel_watch_move_bottom {} { + # Determinate target line + set target_line [$watch_text index end] + set target_line [expr {int($target_line) - 2}] + # Move watch + rightPanel_watch_move $target_line + } + + ## Move current watch to the given line + # @parm Int target_line - target line + # @return void + private method rightPanel_watch_move {target_line} { + # Validate current line value + if {$watch_curLine == 0} {return} + + # Local variables + set cur_idx [expr {$watch_curLine - 1}] ;# index in $watch_addrs -- current line + set trg_idx [expr {$target_line - 1}] ;# index in $watch_addrs -- target line + set addr [lindex $watch_addrs $cur_idx] ;# register address + set name [lindex $watch_data($addr) 0] ;# watch name + set var [lindex $watch_data($addr) 1] ;# textvariable of the value entry + + # Modify variables related to the watch + set watch_addrs [lreplace $watch_addrs $cur_idx $cur_idx] + set watch_addrs [linsert $watch_addrs $trg_idx $addr] + + # Enable the widget + $watch_text configure -state normal + # Change textual content + $watch_text delete $watch_curLine.0 "$watch_curLine.0 + 1 line linestart" + $watch_text insert $target_line.0 "[get_watchAddr $addr] [get_watchName $name]\n" + # Destroy the current entry widget + destroy $watch_text.$var + # Change embedded entry + set entry [watch_create_entry $addr $target_line $var] + $watch_text window create [list $target_line.0 lineend] -window $entry -pady 0 + set len [string length $addr] + if {$len == 4} { + set addr_tag {tag_Xaddr} + } elseif {$len == 3 && ([string index [string trim $addr] 0] == {.})} { + set addr_tag {tag_Baddr} + } elseif {$len == 3} { + set addr_tag {tag_Eaddr} + } { + set addr_tag {tag_addr} + } + # Restore text tags + $watch_text tag add $addr_tag $target_line.0 $target_line.4 + $watch_text tag add tag_name $target_line.5 $target_line.28 + # Disable the widget + $watch_text configure -state disabled + + # Synchronize entry widget content + rightPanel_watch_sync $addr + + # Enable/Disable the entry widget + if {!$watches_enabled} { + $entry configure -state disabled + } + + # Set current line + set watch_curLine $target_line + rightPanel_watch_switch_line $target_line + + # Clear search entry + $watch_search_entry delete 0 end + + # Adjust status modified + set watches_modified 1 + } + + ## Change current line in list of register watches to the line above the current one + # @parm Int lines - number of lines to skip - 1 + # @return void + public method rightPanel_watch_up {lines} { + # Determinate number of last row in the widget + set end [$watch_text index end] + set end [expr {int($end) - 2}] + + # Change current line (logicaly) + set line $watch_curLine + incr line -$lines + if {$line < 1} { + set line 1 + } + + # Change current line (physicaly) + $watch_text see $watch_curLine.0 + rightPanel_watch_switch_line $line + + # Clear search entry + $watch_search_entry delete 0 end + } + + ## Change current line in list of register watches to the line below the current one + # @parm Int lines - number of lines to skip - 1 + # @return void + public method rightPanel_watch_down {lines} { + # Determinate number of last row in the widget + set end [$watch_text index end] + set end [expr {int($end) - 2}] + + # Change current line (logicaly) + set line $watch_curLine + incr line $lines + if {$line > $end} { + set line $end + } + + # Change current line (physicaly) + $watch_text see $watch_curLine.0 + rightPanel_watch_switch_line $line + + # Clear search entry + $watch_search_entry delete 0 end + } + + ## Binding for button "New" (Clears entry widgets at the bottom and unselect current watch) + # @return void + public method rightPanel_watch_new {} { + set end [$watch_text index end] + set end [expr {int($end) - 1}] + rightPanel_watch_switch_line $end + + # Clear search entry + $watch_search_entry delete 0 end + } + + ## Remove the current register watch + # @return void + public method rightPanel_watch_remove {} { + # Determinate register address + set addr [lindex $watch_addrs [expr {$watch_curLine - 1}]] + # Destroy value entry + set var [lindex $watch_data($addr) 1] + destroy $watch_text.$var + # Unregister watch + unset watch_data($addr) + set idx [lsearch $watch_addrs $addr] + set watch_addrs [lreplace $watch_addrs $idx $idx] + + # Remove watch from the text widget + $watch_text configure -state normal + $watch_text delete $watch_curLine.0 "$watch_curLine.0 + 1 line" + $watch_text configure -state disabled + + # Change current line + if {$watch_curLine > [llength $watch_addrs]} { + set watch_curLine [llength $watch_addrs] + } + if {$watch_curLine} { + rightPanel_watch_switch_line $watch_curLine + } + + # Reevaluate button states + watch_disEna_buttons + # Clear search entry + $watch_search_entry delete 0 end + # Adjust status modified + set watches_modified 1 + } + + ## Save watches definition to a file + # @parm String filename - Target filename or an empty string + # @parm Bool force = 0 - Do not ask for overwrite + # @return void + public method rightPanel_watch_save args { + if {!$gui_initialized} {CreateRegWatchesGUI} + + set filename [lindex $args 0] + set force [lindex $args 1] + if {$filename != {}} { + set watch_file_name $filename + } + if {$force != {1}} { + set force 0 + } + + # If no filename specified -> invoke dislog "Save as" + if {$watch_file_name == {}} { + rightPanel_watch_saveas + + # Save file + } { + # Set new filename + if {!$::MICROSOFT_WINDOWS} { ;# POSIX way + if {![regexp "^(~|/)" $watch_file_name]} { + set filename "[$this cget -ProjectDir]/$watch_file_name" + } + } { ;# Microsoft windows way + if {![regexp "^\w:" $watch_file_name]} { + set filename [file join [$this cget -ProjectDir] $watch_file_name] + } + } + + set watch_file_name [file normalize $watch_file_name] + # Adjust file extension + if {![regexp {\.wtc$} $watch_file_name]} { + append watch_file_name {.wtc} + } + + if {[file exists $watch_file_name] && [file isfile $watch_file_name]} { + # Ask user for overwrite existing file + if {!$force && [tk_messageBox \ + -type yesno \ + -icon question \ + -parent . \ + -title [mc "Overwrite file"] \ + -message [mc "A file name '%s' already exists. Are you sure you want to overwrite it ?" [file tail $watch_file_name]] + ] != {yes} + } { + return + } + # Create a backup file + catch { + file rename -force $watch_file_name "$watch_file_name~" + } + } + if {[catch { + set file [open $watch_file_name w 420] + }]} { + if {[winfo exists .fsd]} { + set parent .fsd + } { + set parent . + } + tk_messageBox -type ok \ + -parent $parent -icon warning \ + -title [mc "Error - MCU 8051 IDE"] \ + -message [mc "Unable to access file \"%s\", check your permissions." $watch_file_name] + return + } + + # Write file header + puts $file "# Watches definition file -- ${::APPNAME}" + puts $file "# Date: [clock format [clock seconds] -format {%D}]" + + # Write watches definition + puts -nonewline $file [regsub -all -line {\s+$} [$watch_text get 1.0 end] {}] + + # Finish + close $file + Sbar [mc "Definitions saved to \"%s\"" $watch_file_name] + + # Adjust status modified + set watches_modified 0 + } + } + + ## Invoke dialog "Save as" - Register watches + # @return void + public method rightPanel_watch_saveas {} { + + # Abort if there is already opened some file selection dialog + if {[winfo exists .fsd]} {return} + + # Invoke the dialog + KIFSD::FSD ::fsd \ + -title [mc "Save watches - MCU 8051 IDE"] \ + -directory [$this cget -ProjectDir] \ + -defaultmask 0 -multiple 0 -filetypes { + {{MCU 8051 IDE watch definition} {*.wtc} } + {{All files} {*} } + } + # Save file after press of OK button + ::fsd setokcmd { + set ::filename [::fsd get] + if {$::filename != {} && ![file isdirectory $::filename]} { + ${::X::actualProject} rightPanel_watch_save $::filename + } + } + # Activate the dialog + ::fsd activate + } + + ## Open and process watches definition file + # @parm String filename - name of source file + # @parm Widget parent - GUI parent (for error dialogs) + # @parm Bool clear - Clear watches before loading + # @return Bool - result + public method rightPanel_watch_openfile {filename parent clear} { + if {!$gui_initialized} {CreateRegWatchesGUI} + + # Normalize filename + if {!$::MICROSOFT_WINDOWS} { ;# POSIX way + if {![regexp "^(~|/)" $filename]} { + set filename "[$this cget -projectPath]/$filename" + } + } { ;# Microsoft windows way + if {![regexp "^\w:" $filename]} { + set filename [file join [$this cget -projectPath] $filename] + } + } + set filename [file normalize $filename] + + # Set new watches filename + set watch_file_name $filename + + # Open file + if {[catch { + set file [open $filename r] + }]} then { + tk_messageBox -parent $parent -icon warning -type ok \ + -title [mc "File access error"] \ + -message [mc "Unable to read file '%s'" $filename] + set watch_file_name {} + return 0 + } + + # Verify input data validity + while {![eof $file]} { + set line [gets $file] + + # Skip comments and empty lines + if {[regexp {^\s*#} $line]} {continue} + if {[regexp {^\s*$} $line]} {continue} + + # Local variables + regexp {^\s*\.?\w+} $line addr ;# Register address + regsub {^\s*\.?\w+\s*} $line {} name ;# Watch name + regsub {\s+$} $name {} name + + # Check for address and name validity + if { + ![regexp {^\s*\.?[A-Fa-f0-9]+$} $addr] || + [string length $addr] > 4 || + [string length $name] > 23 + } { + tk_messageBox \ + -title [mc "Corrupted file"] \ + -icon error -type ok -parent $parent \ + -message [mc "file: %s is eighter corrupted or it is not a file in expected format." $filename] + return 0 + } + } + + # Clear watches + if {$clear} { + rightPanel_watch_clear 1 + } + + # Parse input data + seek $file 0 + while {![eof $file]} { + set line [gets $file] + + # Skip comments and empty lines + if {[regexp {^\s*#} $line]} {continue} + if {[regexp {^\s*$} $line]} {continue} + + regexp {^\s*\.?\w+} $line addr ;# Register address + regsub {^\s*\.?\w+\s*} $line {} name ;# Watch name + set addr [string trimleft $addr] + set name [string trimright $name] + + # Create new register watch + rightPanel_watch_add $addr $name + } + + # Deselect all + rightPanel_watch_new + + # Reevaluate button states (icon bar) + watch_disEna_buttons + + # Adjust status modified + set watches_modified 0 + + # Success + close $file + return 1 + } + + ## Invoke dialog "Open file" + # @return void + public method rightPanel_watch_open {} { + # Invoke the dialog + KIFSD::FSD ::fsd \ + -title [mc "Load watches from file - MCU 8051 IDE"] \ + -directory [$this cget -ProjectDir] -autoclose 0 \ + -defaultmask 0 -multiple 0 -filetypes { + {{MCU 8051 IDE watches definition} {*.wtc} } + {{All files} {*} } + } + + # Open file after press of OK button + fsd setokcmd { + # Get chosen file name + set filename [::fsd get] + if {[${X::actualProject} rightPanel_watch_openfile $filename [::fsd get_window_name] 1]} { + ::fsd deactivate + delete object fsd + } + } + + # Activate the dialog + fsd activate + } + + ## Invoke dialog "Import file" + # @return void + public method rightPanel_watch_import {} { + # Invoke the dialog + KIFSD::FSD ::fsd \ + -title [mc "Import file - MCU 8051 IDE"] \ + -directory [$this cget -ProjectDir] -autoclose 0 \ + -defaultmask 0 -multiple 0 -filetypes { + {{Code listing} {*.lst} } + {{MCU 8051 IDE watches definition} {*.wtc} } + {{All files} {*} } + } + + # Open file after press of OK button + fsd setokcmd { + # Get chosen file name + set filename [::fsd get] + if {[${X::actualProject} rightPanel_watch_import_file $filename [::fsd get_window_name]]} { + ::fsd deactivate + delete object fsd + } + } + + # Activate the dialog + fsd activate + } + + ## Import file + # @parm String filename - Name of source file (*.lst or *.wtc) + # @parm Widget parent - GUI parent (for error dialogs) + # @return Bool - result + public method rightPanel_watch_import_file {filename parent} { + if {!$gui_initialized} {CreateRegWatchesGUI} + + # Determinate file type + set filename [file normalize [file join [$this cget -ProjectDir] $filename]] + set file_type 0 + switch -- [file extension $filename] { + {.wtc} { ;# Watches definition file + set file_type 1 + } + {.lst} { ;# Code listing + set file_type 2 + } + default { ;# Try to detect file type by file header + catch { + set file [open $filename r] + if {[string first {# Watches definition file} [gets $file]] == 0} { + set file_type 1 + } + close $file + } + } + } + + # Unknown file type + if {!$file_type} { + tk_messageBox \ + -parent . \ + -type ok \ + -icon warning \ + -title [mc "Unknown file"] \ + -message [mc "Unable to recognize file format"] + return 0 + } + + # ----------------------------------------------------------------------- + ## WTC file - load and exit procedure + # ----------------------------------------------------------------------- + if {$file_type == 1} { + return [rightPanel_watch_openfile $filename $parent 0] + } + + + # ----------------------------------------------------------------------- + # LST file + # ----------------------------------------------------------------------- + + # Try to open file + if {[catch { + set file [open $filename r] + }]} { + tk_messageBox \ + -parent . \ + -type ok \ + -icon warning \ + -title [mc "File access error"] \ + -message [mc "Unable to open file:\n'%s'" $filename] + return 0 + } + + # Parse file + set read_line 0 + set line {} + set name {} + set addr {} + set type {} + set bita 0 + while {![eof $file]} { + set bita 0 + set line [gets $file] + + # Empty line - stop reading + if {![string length [string trimright $line " \f"]]} { + set read_line 0 + continue + + # MCU 8051 IDE Assembler symbol table + } elseif {![string first {SYMBOL TABLE:} $line]} { + set read_line 1 + continue + + # ASEM-51 Assembler symbol table + } elseif {![string first {------------------------------------------------------------} $line]} { + set read_line 2 + continue + } + + + # MCU 8051 IDE Assembler symbol + if {$read_line == 1} { + if {![regexp {^\w+} $line name]} { + continue + } + if {![regexp {[\w\s]+$} $line line]} { + continue + } + set type [lindex $line 0] + + # Ignore all types except address + if {[lindex $line 1] != {ADDR}} { + continue + } + + # Internal data memory + if {$type == {D} || $type == {I}} { + set addr [string range [lindex $line 2] 2 3] + # External data memory (inluding ERAM, EEPROM, etc.) + } elseif {$type == {X}} { + set addr [string range [lindex $line 2] 0 3] + # Bit addressable area + } elseif {$type == {B}} { + set bita 1 + set addr [string range [lindex $line 2] 2 3] + # Another type of memory -> IGNORE + } else { + continue + } + + # Ignore unused symbols + if {[lindex $line end-1] == {NOT} || [lindex $line end-2] == {NOT}} { + continue + } + + # ASEM-51 Assembler symbol + } elseif {$read_line == 2} { + # Remove dangerous characters + regsub -all {\{\}\"\"} $line {} line + + # Ignore unused symbols + if {[llength $line] < 4} { + continue + } + + # Determinate address and symbol name + set type [lindex $line 1] + set addr [lindex $line 2] + set name [lindex $line 0] + + # Accept only internal and external data memory + if {$type != {IDATA} && $type != {DATA} && $type != {XDATA} && $type != {BIT}} { + continue + } + if {$type == {BIT}} { + set bita 1 + } + + # This line is not a part of symbol table + } else { + continue + } + + # Address must be a valid hexadecimal value + if {![string is xdigit -strict $addr]} { + continue + } + + # Exclude SFR's + if {[lsearch -ascii -exact ${::ASMsyntaxHighlight::spec_registers} $name] != -1} { + continue + } + + # Create new register watch + if {[string length $name] > 23} { + set name [string range $name 0 16] + append name {..} + } + + if {$bita} { + set addr .$addr + } + + rightPanel_watch_add $addr $name + set watches_modified 1 + } + + # Finalize + rightPanel_watch_new + watch_disEna_buttons + close $file + return 1 + } + + ## Remove all register watches + # Bool force = 0 - Don't ask user for comfirmation + # @return void + public method rightPanel_watch_clear args { + if {!$gui_initialized} {CreateRegWatchesGUI} + + # Parse arguments + set force [lindex $args 0] + if {$force == {}} { + set force 0 + } + + # Ask user for comfirmation + if {!$force} { + if {[tk_messageBox \ + -parent . \ + -type yesno \ + -icon question \ + -title [mc "Are you sure ?"] \ + -message [mc "Do you really want to clear the panel ?"] + ] != {yes}} { + return + } + } + + # Clear text widget + $watch_text configure -state normal + $watch_text delete 1.0 end + $watch_text configure -state disabled + + # Destroy all embedded entry widgets + foreach addr $watch_addrs { + destroy $watch_text.[lindex $watch_data($addr) 1] + } + + # Clear entries on the bottom bar + set watch_curLine 0 + set ::RightPanel::watch_name${obj_idx} {} + set ::RightPanel::watch_addr${obj_idx} {} + + # Unregister all watches + set watch_addrs {} + catch { + array unset watch_data + } + + # Reevaluate button states (icon bar) + watch_disEna_buttons + + # Adjust status modified + set watches_modified 1 + } + + ## Search for the given string in the list of register watches -- search entry validator + # @parm String content - String to find/validate + # @return Bool - result + public method rightPanel_watch_search_validate {content} { + if {$search_val_in_progress} {return 0} + set search_val_in_progress 1 + + # Enable/Disable button "Clear search entry" + if {$content == {}} { + $watch_search_clear configure -state disabled + } { + $watch_search_clear configure -state normal + } + + # Validate search string + if {[regexp {^\s*$} $content]} { + $watch_search_entry configure -style TEntry + set search_val_in_progress 0 + return 1 + } + if {[string length $content] > 23} { + set search_val_in_progress 0 + return 0 + } + + # Search for the given string + set i 1 + set content [string trimright $content] + set content [string tolower $content] + foreach addr $watch_addrs { + if {![string first $content [string tolower [lindex $watch_data($addr) 0]]]} { + $watch_search_entry configure -style StringFound.TEntry + rightPanel_watch_switch_line $i + focus $watch_search_entry + set search_val_in_progress 0 + return 1 + } + incr i + } + + # String not found + $watch_search_entry configure -style StringNotFound.TEntry + set search_val_in_progress 0 + return 1 + } + + ## Syncronize all register watches + # @return void + public method rightPanel_watch_sync_all {} { + if {!$gui_initialized} {CreateRegWatchesGUI} + + # Iterate over addresses + foreach addr $watch_addrs { + # Synchronize + rightPanel_watch_sync $addr + # Clear highligh + set var [lindex $watch_data($addr) 1] + $watch_text.$var configure -fg ${Simulator::normal_color} + } + } + + ## Read value from simulator engine + # @parm String addr - hexadecimal register address + # @return String - hexadecimal value or {--} + private method read_from_simulator {addr} { + ## Determinate address type (Bit / Internal / Enternal / Expanded) + if {[string index $addr 0] == {.}} { + set addr [string replace $addr 0 0] + if {![string length $addr]} { + return {--} + } + set bit_addr 1 + } { + set bit_addr 0 + } + set len [string length $addr] + set val {--} + set addr_dec [expr "0x$addr"] + + # Bit + if {$bit_addr} { + if {$addr_dec > 0x7F} { + if {[$this simulator_is_sfr_avaliable [$this getRegOfBit $addr_dec]]} { + set val [$this getBit $addr_dec] + } + } { + set val [$this getBit $addr_dec] + } + + # Internal RAM + } elseif {$len < 3} { + # Normalize address + if {$len == 1} { + set addr "0$addr" + } + # Get register value + if {$addr_dec < [lindex [$this cget -procData] 3]} { + set val [$this getData $addr_dec] + } + + # Expanded RAM + } elseif {$len == 3} { + if {$addr_dec < [lindex [$this cget -procData] 8]} { + set val [$this getEram $addr_dec] + } + + # External RAM + } elseif {$len == 4} { + if {$addr_dec < [$this cget -P_option_mcu_xdata]} { + set val [$this getXdata $addr_dec] + } + } + + return $val + } + + ## Synchronize all bits in the specified SFR + # @parm Int dec_addr - SFR register address + # @return void + public method rightPanel_watch_sync_sfr {dec_addr} { + if {$validator_engaged} { + return + } + if {$dec_addr % 8} { + return + } + + for {set i 0} {$i < 8} {incr i} { + rightPanel_watch_sync .[format %X $dec_addr] + incr dec_addr + } + } + + ## Synchronize one register watch + # @parm String addr - hexadecimal register address + # @return Bool - result + public method rightPanel_watch_sync {addr} { + if {$validator_engaged} {return 1} + if {!$gui_initialized} {CreateRegWatchesGUI} + + # Detect bit address + if {[string index $addr 0] == {.}} { + set bit_addr 1 + } { + set bit_addr 0 + } + + # Get register value + set val [read_from_simulator $addr] + + # Synchronize bits in the given register + if {!$validator_engaged && [string length $addr] == 2} { + set dec_addr [expr "0x$addr"] + + if {$dec_addr >= 0x20 && $dec_addr <= 0x2F} { + set dec_addr [expr {($dec_addr - 0x20) * 8}] + for {set i 0} {$i < 8} {incr i} { + + set hex_addr [format %X $dec_addr] + if {[string length $hex_addr] == 1} { + set hex_addr "0$hex_addr" + } + rightPanel_watch_sync .$hex_addr + incr dec_addr + } + } + } + + # Check for watch presence + if {[lsearch -ascii -exact $watch_addrs $addr] == -1} { + return 0 + } + + # Normalize register value + if {!$bit_addr && [string length $val] == 1} { + set val "0$val" + } + + set var [lindex $watch_data($addr) 1] + set path $watch_text.$var ;# Path to watch entry widget + + # Determinate original value + set original_val [$watch_text.$var get] + + # Highlight value entry + if { "0x$original_val" != "0x$val"} { + $path configure -fg ${Simulator::highlight_color} + } + + # Set new entry value + set validator_engaged 1 + $watch_text.$var delete 0 end + $watch_text.$var insert 0 $val + set validator_engaged 0 + + # Synchronize with help window + help_window_update $addr $val + + # Done ... + return 1 + } + + ## Enable/Disable buttons on watches icon bar + # @return void + private method watch_disEna_buttons {} { + if {!$gui_initialized} {return} + + if {[$watch_text index end] == {2.0}} { + set state {disabled} + } { + set state {normal} + } + + if {$conf_menu != {}} { + $conf_menu entryconfigure [::mc "Sort by"] -state $state + $conf_menu entryconfigure [::mc "Remove all"] -state $state + } + } + + ## Get status modified for register watches + # @return Bool - true if register watches were modified + public method rightPanel_watch_modified {} { + return $watches_modified + } + + ## Invoke register watches popup menu + # @parm Int X - absolute X coordinate + # @parm Int Y - absolute Y coordinate + # @parm Int x - relative X coordinate + # @parm Int y - relative Y coordinate + # @return void + public method rightPanel_watch_popupmenu {X Y x y} { + # Change current line + rightPanel_watch_click $x $y + + ## Enable/Disable menu items + + # If address entry is not empty -> disable all + set addr [subst "\$::RightPanel::watch_addr${obj_idx}"] + if {$addr != {}} { + set end [$watch_text index end] + } { + set end {2.0} + } + # Empty list + if {$end == {2.0}} { + $watch_menu entryconfigure [::mc "Move top"] -state disabled + $watch_menu entryconfigure [::mc "Move up"] -state disabled + $watch_menu entryconfigure [::mc "Move down"] -state disabled + $watch_menu entryconfigure [::mc "Move bottom"] -state disabled + $watch_menu entryconfigure [::mc "Remove"] -state disabled + $watch_menu entryconfigure [::mc "Remove all"] -state disabled + # One item + } elseif {$end == {3.0}} { + $watch_menu entryconfigure [::mc "Move top"] -state disabled + $watch_menu entryconfigure [::mc "Move up"] -state disabled + $watch_menu entryconfigure [::mc "Move down"] -state disabled + $watch_menu entryconfigure [::mc "Move bottom"] -state disabled + $watch_menu entryconfigure [::mc "Remove"] -state normal + $watch_menu entryconfigure [::mc "Remove all"] -state normal + # More items + } else { + # First item + if {$watch_curLine == 1} { + $watch_menu entryconfigure [::mc "Move top"] -state disabled + $watch_menu entryconfigure [::mc "Move up"] -state disabled + $watch_menu entryconfigure [::mc "Move down"] -state normal + $watch_menu entryconfigure [::mc "Move bottom"] -state normal + # Last item + } elseif {$watch_curLine == ($end - 2)} { + $watch_menu entryconfigure [::mc "Move top"] -state normal + $watch_menu entryconfigure [::mc "Move up"] -state normal + $watch_menu entryconfigure [::mc "Move down"] -state disabled + $watch_menu entryconfigure [::mc "Move bottom"] -state disabled + # Any other item + } else { + $watch_menu entryconfigure [::mc "Move top"] -state normal + $watch_menu entryconfigure [::mc "Move up"] -state normal + $watch_menu entryconfigure [::mc "Move down"] -state normal + $watch_menu entryconfigure [::mc "Move bottom"] -state normal + } + $watch_menu entryconfigure [::mc "Remove"] -state normal + $watch_menu entryconfigure [::mc "Remove all"] -state normal + } + + # Invoke popup menu + tk_popup $watch_menu $X $Y + } + + + ## Create bindings for defined key shortcuts -- register watches + # @return void + public method rightPanel_watch_shortcuts_reevaluate {} { + if {!$gui_initialized} {return} + foreach addr $watch_addrs { + watch_entry_shortcuts_reset watch_text.[lindex $watch_data($addr) 1] + } + } + + ## Set flag enabled + # @parm Bool bool - New value + # @return void + public method right_panel_watches_set_enabled {bool} { + set enabled $bool + } +} + +set ::RegWatches::menu_autoload [lindex $::CONFIG(REGWATCHES_CONFIG) 0] +set ::RegWatches::sorting_order [lindex $::CONFIG(REGWATCHES_CONFIG) 1] |