summaryrefslogtreecommitdiff
path: root/lib/utilities/hexeditdlg.tcl
diff options
context:
space:
mode:
authorAndrej Shadura <andrewsh@debian.org>2018-05-08 15:59:29 +0200
committerAndrej Shadura <andrewsh@debian.org>2018-05-08 15:59:29 +0200
commit5b8466f7fae0e071c0f4eda13051c93313910028 (patch)
tree7061957f770e5e245ba00666dad912a2d44e7fdc /lib/utilities/hexeditdlg.tcl
Import Upstream version 1.3.7
Diffstat (limited to 'lib/utilities/hexeditdlg.tcl')
-rwxr-xr-xlib/utilities/hexeditdlg.tcl1793
1 files changed, 1793 insertions, 0 deletions
diff --git a/lib/utilities/hexeditdlg.tcl b/lib/utilities/hexeditdlg.tcl
new file mode 100755
index 0000000..42175f1
--- /dev/null
+++ b/lib/utilities/hexeditdlg.tcl
@@ -0,0 +1,1793 @@
+#!/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
+# Prodides hexadecimal editor for external data and program memory.
+# This editor uses dynamic data loading.
+# --------------------------------------------------------------------------
+
+class HexEditDlg {
+ common count 0 ;# Instance counter
+ common win_pos {+0+0} ;# Window position (+X+Y)
+ common mode {hex} ;# View mode {hex dec oct}
+ common cell {0} ;# Current cell (0 - 0xFFFF)
+ common current_view {left} ;# Focused view {left right}
+ # Font for mode combobox
+ common mode_cb_font [font create \
+ -family {Helvetica} \
+ -size -17 \
+ -weight bold \
+ ]
+ # General normal size bold font
+ common bold_font [font create \
+ -family $::DEFAULT_FIXED_FONT \
+ -size -12 \
+ -weight bold \
+ ]
+ # Status bar tips for main menu for XDATA mode
+ common HELPFILE_XDATA {
+ {
+ {Load IHEX8 file into editor and simulator XDATA memory}
+ {}
+ {Save current content of XDATA memory to IHEX8 file}
+ {Save current document under a different name}
+ {}
+ {Reload data from simulator XDATA memory}
+ {}
+ {Exit editor}
+ } {
+ {Copy selected text to clipboard}
+ {Paste clipboard contents}
+ {}
+ {}
+ {Invoke dialog for searching strings in the text}
+ {Find next occurence of the search string}
+ {Find previous occurence of the search string}
+ } {
+ {Switch view mode to hexadecimal}
+ {Switch view mode to decimal}
+ {Switch view mode to octal}
+ }
+ }
+ # Status bar tips for main menu for CODE mode
+ common HELPFILE_CODE {
+ {
+ {Load IHEX8 file into editor and simulator XDATA memory}
+ {Save current content of program (CODE) memory to IHEX8 file}
+ {}
+ {Save}
+ {Save current document under a different name}
+ {}
+ {Exit editor}
+ } {
+ {Copy selected text to clipboard}
+ {Paste clipboard contents}
+ {}
+ {}
+ {Invoke dialog for searching strings in the text}
+ {Find next occurence of the search string}
+ {Find previous occurence of the search string}
+ } {
+ {Switch view mode to hexadecimal}
+ {Switch view mode to decimal}
+ {Switch view mode to octal}
+ }
+ }
+
+ ## PRIVATE
+ private variable project ;# Object: Project realted to this editor
+ private variable type ;# String: HexEditor type (one of {xdata code})
+ private variable hexeditor ;# Object: Hexadecimal editor pseudowidget
+ private variable win ;# Widget: Dialog toplevel window
+ private variable mainmenu ;# ID of dialog main menu
+ private variable edit_menu ;# ID of dialog edit menu
+ private variable mode_combo_box ;# ID of mode combobox
+ private variable right_sbar_label ;# ID of right label on dialog status bar
+ private variable middle_sbar_label ;# ID of middle label on dialog status bar
+ private variable left_sbar_label ;# ID of left label on dialog status bar
+ private variable current_cell ;# Current cell (0 - 0xFFFF)
+ private variable validation_ena 0 ;# Bool: EntryBox validation enable
+ private variable dec_val_entry ;# EntryBox: Value - Decimal
+ private variable oct_val_entry ;# EntryBox: Value - Octal
+ private variable hex_val_entry ;# EntryBox: Value - Hexadecimal
+ private variable bin_val_entry ;# EntryBox: Value - Binary
+ private variable dec_addr_entry ;# EntryBox: Address - Decimal
+ private variable oct_addr_entry ;# EntryBox: Address - Octal
+ private variable hex_addr_entry ;# EntryBox: Address - Hexadecimal
+ private variable bin_addr_entry ;# EntryBox: Address - Binary
+ private variable sub_call_but ;# Button: Call subprogram
+ private variable prg_jump_but ;# Button: Perform program jump
+ private variable obj_idx ;# Index of the current instance
+ private variable loaded_lines {} ;# Map of loaded lines (for dynamic data loading)
+ private variable opened_file {} ;# Name of opened file
+ private variable modified 0 ;# Bool: fag modified
+ private variable capacity 0 ;# Int: Memory capacity
+ private variable last_PC -1 ;# Int: Last position of PC pointer
+ private variable last_PC_length 0 ;# Int: Length of the last PC pointer
+ private variable last_PC_d -1 ;# Int: Last position of PC pointer (func move_program_pointer_directly)
+ private variable last_PC_length_d 0 ;# Int: Length of the last PC pointer (func move_program_pointer_directly)
+ private variable pre_last_PC -1 ;# Int: Last value of $last_PC
+ private variable pre_last_PC_length 0 ;# Int: Last value of $last_PC_length
+
+ ## Object constructor
+ # @parm Object _project - Parent project
+ # @parm String _type - Type of contents (one of {xdata code eram eeprom uni})
+ constructor {_project _type} {
+ # Initalize object variables
+ set project $_project
+ set type $_type
+ set obj_idx $count
+ set win [toplevel .hexeditdlg${obj_idx} -class {Hex Editor} -bg {#EEEEEE}]
+ set loaded_lines [string repeat [string repeat 0 0xFF] 0xFF]
+
+ incr count ;# Increment instance counter
+
+ # Determinate memory capacity
+ switch -- $type {
+ {code} {
+ set capacity [$project cget -P_option_mcu_xcode]
+ incr capacity [expr {[lindex [$project cget -procData] 2] * 1024}]
+ }
+ {xdata} {
+ set capacity [$project cget -P_option_mcu_xdata]
+ }
+ {eram} {
+ set capacity [lindex [$project cget -procData] 8]
+ }
+ {eeprom} {
+ set capacity [lindex [$project cget -procData] 32]
+ }
+ {uni} {
+ set capacity 0x10000
+ }
+ }
+
+ # Create dialog frames
+ set tool_bar_frame [frame $win.tool_bar] ;# Toolbar
+ set middle_frame $win.middle_frame ;# Left view and right view
+ set bottom_frame [frame $win.bottom_frame] ;# EntryBoxes: Value & Address
+ set statusbar_frame [frame $win.statusbar_frame] ;# Dialog statusbar
+
+ # Create dialog componets
+ create_status_bar $statusbar_frame
+ create_main_menu
+ create_tool_bar $tool_bar_frame
+ create_middle_bottom_frame $middle_frame $bottom_frame
+ create_main_win_bindings
+
+ # Add items "LJMP" and "LCALL" to popup menu
+ if {$type == {code}} {
+ if {[$project is_frozen]} {
+ set state normal
+ } {
+ set state disabled
+ }
+
+ [$hexeditor get_popup_menu] add separator
+ [$hexeditor get_popup_menu] add command -label [mc "LJMP this_address"] \
+ -underline 1 -command "$this prog_jump" -state $state \
+ -compound left -image ::ICONS::16::exec
+ [$hexeditor get_popup_menu] add command -label [mc "LCALL this_address"]\
+ -underline 1 -command "$this sub_call" -state $state \
+ -compound left -image ::ICONS::16::exec
+ }
+
+ # Load data from simulator engine to current visible area
+ load_data_to_current_view
+
+ # Fill EntryBoxes
+ if {$cell >= $capacity} {
+ set current_cell [expr {$capacity - 1}]
+ } {
+ set current_cell $cell
+ }
+ set value [$hexeditor get_values $current_cell $current_cell]
+ fill_entries {} val $value
+ fill_entries {} addr $current_cell
+ set validation_ena 1
+
+ # Pack dialog frames
+ pack $tool_bar_frame -fill x -anchor w
+ pack $middle_frame -anchor nw -after $tool_bar_frame -pady 10
+ pack $bottom_frame -anchor w -after $middle_frame
+ pack $statusbar_frame -side bottom -fill x -after $bottom_frame
+
+ # Set window title
+ if {$type == {code}} {
+ set window_icon {kcmmemory_C}
+ wm title $win "[mc {Code memory}] - $project - MCU 8051 IDE"
+ } elseif {$type == {eram}} {
+ set window_icon {kcmmemory_E}
+ wm title $win "[mc {Expanded RAM}] - $project - MCU 8051 IDE"
+ } elseif {$type == {eeprom}} {
+ set window_icon {kcmmemory_P}
+ wm title $win "[mc {Data EEPROM}] - $project - MCU 8051 IDE"
+ } elseif {$type == {xdata}} {
+ set window_icon {kcmmemory_X}
+ wm title $win "[mc {XDATA memory}] - $project - MCU 8051 IDE"
+ } else {
+ set window_icon {ascii}
+ wm title $win "[mc {untitled}] - [mc {Hexadecimal editor}] - MCU 8051 IDE"
+ }
+
+ # Set window geometry
+ wm resizable $win 0 0
+ if {$mode == {hex}} {
+ wm geometry $win ${win_pos}
+ } {
+ wm geometry $win ${win_pos}
+ }
+
+ # Finalize window configuration
+ wm iconphoto $win ::ICONS::16::$window_icon
+ if {$type == {uni}} {
+ wm protocol $win WM_DELETE_WINDOW "$this quit"
+ } {
+ wm protocol $win WM_DELETE_WINDOW \
+ [list ::X::close_hexedit $type $project]
+ }
+ }
+
+ ## Object destructor
+ destructor {
+ # Save current window parameters
+ set win_pos [wm geometry $win]
+ set win_pos [split $win_pos {+}]
+ set win_pos "+[lindex $win_pos 1]+[lindex $win_pos 2]"
+ set cell [$hexeditor getCurrentCell]
+ set current_view [$hexeditor getCurrentView]
+
+ # Remove dialog window and uset its variables
+ destroy $win
+ unset ::HexEditDlg::dec_val_${obj_idx}
+ unset ::HexEditDlg::hex_val_${obj_idx}
+ unset ::HexEditDlg::oct_val_${obj_idx}
+ unset ::HexEditDlg::bin_val_${obj_idx}
+ unset ::HexEditDlg::dec_addr_${obj_idx}
+ unset ::HexEditDlg::hex_addr_${obj_idx}
+ unset ::HexEditDlg::oct_addr_${obj_idx}
+ unset ::HexEditDlg::bin_addr_${obj_idx}
+ unset ::HexEditDlg::mode_${obj_idx}
+ }
+
+ ## Create key event bindings for dialog window
+ # @return void
+ private method create_main_win_bindings {} {
+ foreach widget [list $win [$hexeditor getLeftView] [$hexeditor getRightView]] {
+ bind $widget <Control-Key-o> "$this openhex; break"
+ bind $widget <Control-Key-s> "$this save; break"
+ bind $widget <Control-Key-S> "$this saveas; break"
+ bind $widget <Key-F5> "$this reload; break"
+ bind $widget <Control-Key-q> "$this quit; break"
+ }
+ }
+
+ ## Create main dialog menu
+ # @return voi
+ private method create_main_menu {} {
+ ## Create menu widgets
+ # Main
+ set mainmenu [menu $win.mainmenu \
+ -bd 0 -tearoff 0 -bg {#EEEEEE} \
+ -activeforeground {#6666FF} \
+ -activebackground {#EEEEEE} \
+ ]
+ set file_menu [menu $mainmenu.file_menu -tearoff 0] ;# Main -> File
+ set edit_menu [menu $mainmenu.edit_menu -tearoff 0] ;# Main -> Edit
+ set mode_menu [menu $mainmenu.mode_menu -tearoff 0] ;# Main -> Mode
+
+ # Create menu event bindings for purpose of status bar tips
+ bind $file_menu <<MenuSelect>> "$this menu_sbar_show 0 \[%W index active\]"
+ bind $edit_menu <<MenuSelect>> "$this menu_sbar_show 1 \[%W index active\]"
+ bind $mode_menu <<MenuSelect>> "$this menu_sbar_show 2 \[%W index active\]"
+ bind $file_menu <Leave> "$this sbar_show {}"
+ bind $edit_menu <Leave> "$this sbar_show {}"
+ bind $mode_menu <Leave> "$this sbar_show {}"
+
+ # Create File menu
+ if {$type == {code}} {
+ $file_menu add command -label "Open ADF" -compound left \
+ -command "$this opensim" -underline 0 \
+ -image ::ICONS::16::fileopen
+ }
+ $file_menu add command -label "Open IHEX8" -compound left \
+ -accelerator "Ctrl+O" -command "$this openhex" \
+ -image ::ICONS::16::fileopen -underline 1
+ $file_menu add separator
+ $file_menu add command -label "Save" -compound left \
+ -accelerator "Ctrl+S" -command "$this save" \
+ -image ::ICONS::16::filesave -underline 0
+ $file_menu add command -label "Save as" -compound left \
+ -accelerator "Ctrl+Shift+S" -command "$this saveas" \
+ -image ::ICONS::16::filesaveas -underline 1
+ $file_menu add separator
+ if {$type != {code}} {
+ $file_menu add command -label "Reload" -compound left \
+ -accelerator "F5" -command "$this reload" \
+ -image ::ICONS::16::reload -underline 1
+ $file_menu add separator
+ }
+ $file_menu add command -label "Exit" -compound left \
+ -accelerator "Ctrl+Q" -command "$this quit" \
+ -image ::ICONS::16::exit -underline 1
+
+ # Create Edit menu
+ $edit_menu add command -label "Copy" -compound left \
+ -accelerator "Ctrl+C" -command "$this text_copy" \
+ -image ::ICONS::16::editcopy -underline 0
+ $edit_menu add command -label "Paste" -compound left \
+ -accelerator "Ctrl+V" -command "$this text_paste" \
+ -image ::ICONS::16::editpaste -underline 0
+ $edit_menu add separator
+ $edit_menu add command -label "Find" -compound left \
+ -accelerator "Ctrl+F" -command "$this find_string 0" \
+ -image ::ICONS::16::find -underline 0
+ $edit_menu add command -label "Find next" -compound left \
+ -accelerator "F3" -command "$this find_string 1" \
+ -image ::ICONS::16::1downarrow -underline 5
+ $edit_menu add command -label "Find previous" -compound left \
+ -accelerator "Shift+F3" -command "$this find_string 2" \
+ -image ::ICONS::16::1uparrow -underline 8
+
+ # Create Mode menu
+ set ::HexEditDlg::mode_${obj_idx} $mode
+ $mode_menu add radiobutton -label "HEX" \
+ -variable ::HexEditDlg::mode_${obj_idx} \
+ -indicatoron 0 -compound left -image ::ICONS::raoff \
+ -selectimage ::ICONS::raon -value {hex} -underline 0 \
+ -command [list $this adjust_mode]
+ $mode_menu add radiobutton -label "DEC" \
+ -variable ::HexEditDlg::mode_${obj_idx} \
+ -indicatoron 0 -compound left -image ::ICONS::raoff \
+ -selectimage ::ICONS::raon -value {dec} -underline 0 \
+ -command [list $this adjust_mode]
+ $mode_menu add radiobutton -label "OCT" \
+ -variable ::HexEditDlg::mode_${obj_idx} \
+ -indicatoron 0 -compound left -image ::ICONS::raoff \
+ -selectimage ::ICONS::raon -value {oct} -underline 0 \
+ -command [list $this adjust_mode]
+
+ # Create Main menu
+ $mainmenu add cascade -label "File" -underline 0 -menu $file_menu
+ $mainmenu add cascade -label "Edit" -underline 0 -menu $edit_menu
+ $mainmenu add cascade -label "Mode" -underline 0 -menu $mode_menu
+ $win configure -menu $mainmenu
+ }
+
+ ## Create dialog toolbar
+ # @parm Widget frame -target frame
+ # @return void
+ private method create_tool_bar {frame} {
+ # Create toolbar frame
+ set toolbar_frame [frame $frame.toolbar]
+ # - Button "Open Hex"
+ pack [ttk::button $toolbar_frame.openhex \
+ -command "$this openhex" \
+ -image ::ICONS::22::fileopen \
+ -style Flat.TButton \
+ ] -side left -padx 2
+ DynamicHelp::add $toolbar_frame.openhex -text [mc "Load IHEX8 file"]
+ set_sbar_tip $toolbar_frame.openhex [mc "Open file"]
+ # - Separator
+ pack [ttk::separator $toolbar_frame.sep0 \
+ -orient vertical \
+ ] -side left -padx 4 -fill y -expand 1
+ # - Button "Save"
+ pack [ttk::button $toolbar_frame.save \
+ -command "$this save" \
+ -image ::ICONS::22::filesave \
+ -style Flat.TButton \
+ ] -side left -padx 2
+ DynamicHelp::add $toolbar_frame.save -text [mc "Save current data to IHEX8 file"]
+ set_sbar_tip $toolbar_frame.save [mc "Save file"]
+ # - Button "Save as"
+ pack [ttk::button $toolbar_frame.saveas \
+ -command "$this saveas" \
+ -image ::ICONS::22::filesaveas \
+ -style Flat.TButton \
+ ] -side left -padx 2
+ DynamicHelp::add $toolbar_frame.saveas -text [mc "Save current data to IHEX8 file under a different name"]
+ set_sbar_tip $toolbar_frame.saveas [mc "Save as"]
+ # - Separator
+ pack [ttk::separator $toolbar_frame.sep1 \
+ -orient vertical \
+ ] -side left -padx 4 -fill y -expand 1
+ if {$type != {code}} {
+ # - Button "Reload"
+ pack [ttk::button $toolbar_frame.reload \
+ -command "$this reload" \
+ -image ::ICONS::22::reload \
+ -style Flat.TButton \
+ ] -side left -padx 2
+ DynamicHelp::add $toolbar_frame.reload -text [mc "Reload data from simulator"]
+ set_sbar_tip $toolbar_frame.reload [mc "Reload"]
+ # - Separator
+ pack [ttk::separator $toolbar_frame.sep2 \
+ -orient vertical \
+ ] -side left -padx 4 -fill y -expand 1
+ }
+ # - Button "Exit"
+ pack [ttk::button $toolbar_frame.exit \
+ -style Flat.TButton \
+ -command "$this quit" \
+ -image ::ICONS::22::exit \
+ ] -side left -padx 2
+ DynamicHelp::add $toolbar_frame.exit -text [mc "Exit editor"]
+ set_sbar_tip $toolbar_frame.exit [mc "Exit"]
+
+ pack $toolbar_frame -side left -anchor w
+
+ # - Mode ComboBox
+ set mode_combo_box [ttk::combobox $frame.mode_cb \
+ -values {HEX DEC OCT} \
+ -state readonly \
+ -font $mode_cb_font \
+ -width 4 \
+ ]
+ bind $mode_combo_box <<ComboboxSelected>> [list $this switch_mode]
+ DynamicHelp::add $mode_combo_box -text [mc "Current view mode"]
+ set_sbar_tip $frame.mode_cb \
+ [mc "View mode"]
+ $mode_combo_box current [lsearch {hex dec oct} $mode]
+ pack $mode_combo_box -side right -anchor e -fill y -expand 0
+ }
+
+ ## Set status tip for the given widget
+ # @parm Widget wdg - Target widget
+ # @parm String txt - Status tip
+ # @return void
+ private method set_sbar_tip {wdg txt} {
+ bind $wdg <Enter> [list $this sbar_show $txt]
+ bind $wdg <Leave> [list $this sbar_show {}]
+ }
+
+ ## Create hexeditor and entryboxes for address and value
+ # @parm Widget middle_frame - Frame for hexeditor
+ # @parm Widget bottom_frame - Frame for entryboxes
+ # @return void
+ private method create_middle_bottom_frame {middle_frame bottom_frame} {
+ ## Create and configure HexEditor
+ set hg [expr {$capacity / 16}]
+ if {[expr {$capacity % 16}]} {
+ incr hg
+ }
+ set hexeditor [HexEditor editor${obj_idx} $middle_frame 16 $hg 4 $mode 1 0 16 $capacity]
+ if {$current_view == {left}} {
+ $hexeditor focus_left_view
+ } {
+ $hexeditor focus_right_view
+ }
+ $hexeditor setCurrentCell $cell
+ $hexeditor bindCellEnter "$this change_right_stat_bar_addr"
+ $hexeditor bindCellLeave "$this change_right_stat_bar_addr {}"
+ $hexeditor bindCurrentCellChanged "$this current_cell_changed"
+ $hexeditor bindCellValueChanged "$this cell_value_changed"
+ $hexeditor bindScrollAction "$this load_data_to_current_view"
+
+ # Create labelframes for Value & Address
+ set value_lframe [ttk::labelframe $bottom_frame.value_label_frame \
+ -text [mc "VALUE"] -padding 5 \
+ ]
+ set address_lframe [ttk::labelframe $bottom_frame.address_label_frame \
+ -text [mc "ADDRESS"] -padding 5 \
+ ]
+
+ # Create entryboxes
+ set i 0
+ set width [list 4 4 9 9 8 8 17 17]
+ foreach valtype {val addr} frm [list $value_lframe $address_lframe] {
+ foreach radix {dec oct hex bin} {
+ set ${radix}_${valtype}_entry [ttk::entry $frm.${radix}_${valtype}_entry \
+ -width [lindex $width $i] \
+ -validate all \
+ -textvariable ::HexEditDlg::${radix}_${valtype}_${obj_idx} \
+ -validatecommand [list $this validate_entry ${valtype} ${radix} %P] \
+ ]
+ bindtags $frm.${radix}_${valtype}_entry \
+ [list $frm.${radix}_${valtype}_entry TEntry $win all .]
+ incr i
+ }
+ }
+
+ # Pack entry boxes and create labels for them
+ grid [label $value_lframe.dec_label -text [mc "DEC: "]] -row 0 -column 0
+ grid [label $value_lframe.oct_label -text [mc "OCT: "]] -row 1 -column 0
+ grid [label $value_lframe.hex_label -text [mc "HEX: "]] -row 0 -column 3
+ grid [label $value_lframe.bin_label -text [mc "BIN: "]] -row 1 -column 3
+ grid $dec_val_entry -row 0 -column 1 -sticky e
+ grid $oct_val_entry -row 1 -column 1 -sticky e
+ grid $hex_val_entry -row 0 -column 4 -sticky e
+ grid $bin_val_entry -row 1 -column 4 -sticky e
+ grid columnconfigure $value_lframe 2 -minsize 10
+
+ grid [label $address_lframe.dec_label -text [mc "DEC: "]] -row 0 -column 0
+ grid [label $address_lframe.oct_label -text [mc "OCT: "]] -row 1 -column 0
+ grid [label $address_lframe.hex_label -text [mc "HEX: "]] -row 0 -column 3
+ grid [label $address_lframe.bin_label -text [mc "BIN: "]] -row 1 -column 3
+ grid $dec_addr_entry -row 0 -column 1 -sticky e
+ grid $oct_addr_entry -row 1 -column 1 -sticky e
+ grid $hex_addr_entry -row 0 -column 4 -sticky e
+ grid $bin_addr_entry -row 1 -column 4 -sticky e
+ grid columnconfigure $address_lframe 2 -minsize 10
+
+ # Create buttons "Call" and "Jump"
+ if {$type == {code}} {
+ if {[$project is_frozen]} {
+ set state normal
+ } {
+ set state disabled
+ }
+
+ set prg_jump_but [ttk::button $address_lframe.prg_jump_but \
+ -text [mc "LJMP"] \
+ -state $state \
+ -command "$this prog_jump" \
+ -width 6 \
+ ]
+ DynamicHelp::add $prg_jump_but -text [mc "Perform program jump"]
+ set_sbar_tip $prg_jump_but [mc "Program jump"]
+ set sub_call_but [ttk::button $address_lframe.sub_call_but \
+ -text [mc "LCALL"] \
+ -state $state \
+ -command "$this sub_call" \
+ -width 6 \
+ ]
+ DynamicHelp::add $sub_call_but -text [mc "Perform subprogram call"]
+ set_sbar_tip $sub_call_but [mc "Subprogram call"]
+
+ grid [ttk::separator $address_lframe.sep -orient vertical] \
+ -row 0 -column 5 -sticky ns -padx 5 -rowspan 2
+ grid $prg_jump_but -row 0 -column 6 -sticky we
+ grid $sub_call_but -row 1 -column 6 -sticky we
+ }
+
+ pack $value_lframe -side left -padx 10
+ pack $address_lframe -side left -padx 10
+ }
+
+ ## Create dialog status bar
+ # @parm Widget frame - Frame for the status bar
+ # @return void
+ private method create_status_bar {frame} {
+ # Create status bar labels
+ set left_sbar_label [label $frame.left -anchor w]
+ set middle_sbar_label [Label $frame.middle]
+ set right_sbar_label [label $frame.right \
+ -fg {#0000FF} -font $bold_font -width 6 \
+ ]
+
+ # Set filename to "untitled" if editor is universal
+ $middle_sbar_label configure -text {untitled}
+
+ # Pack status bar labels
+ pack $left_sbar_label -side left -fill x -expand 1 -anchor w
+ pack $middle_sbar_label -side left -fill none -anchor w -after $left_sbar_label -padx 10
+ pack [label $frame.left_left \
+ -text [mc "Cursor:"] \
+ -font $bold_font \
+ -fg {#555555} \
+ ] -side left -after $middle_sbar_label
+ pack $right_sbar_label -side left -after $frame.left_left
+
+ # Set status tips for right part of the status bar
+ set_sbar_tip $frame.left_left [mc "Address of entry under mouse cursor"]
+ set_sbar_tip $right_sbar_label [mc "Address of entry under mouse cursor"]
+
+ # Initialize pointer address display
+ change_right_stat_bar_addr {}
+ }
+
+ ## Write value to simulator engine and synchronize with all watchers
+ # @parm Int addr - Target address
+ # @parm int val - Register value
+ # @return void
+ private method write_to_simulator {addr val} {
+ if {$type == {uni}} {
+ return
+ }
+
+ # XRAM or ERAM
+ if {$type != {code}} {
+ set hex_addr [format "%X" $addr]
+ set len [string length $hex_addr]
+ if {$len < 4} {
+ set hex_addr "[string repeat 0 [expr {4 - $len}]]$hex_addr"
+ }
+ if {$type == {xdata}} {
+ $project setXdataDEC $addr $val
+ } elseif {$type == {eeprom}} {
+ $project setEepromDEC $addr $val
+ } else {
+ $project setEramDEC $addr $val
+ }
+ $project rightPanel_watch_sync $hex_addr
+
+ # Code memory
+ } {
+ $project setCodeDEC $addr $val
+ }
+ }
+
+ ## Set flag modified and adjust dialog window title
+ # @parm Bool bool - New flag value
+ # @return void
+ private method setModified {bool} {
+ if {$opened_file == {} || $modified == $bool} {
+ return
+ }
+ set modified $bool
+ if {$modified} {
+ wm title $win "\[modified\] [wm title $win]"
+ } {
+ wm title $win [string range [wm title $win] 11 end]
+ }
+ }
+
+
+ ## Parse given data (IHEX-8 and load it into the editor + sync with external components)
+ # @parm String hex_data - input data
+ # @return void
+ private method readHex {hex_data} {
+ # Any EOL -> LF
+ regsub -all {\r\n?} $hex_data "\n" hex_data
+ # Split by lines
+ set hex_data [split $hex_data "\n"]
+
+ # Local variables
+ set pointer 0 ;# Current address
+ set line_number 0 ;# Number of the current line
+ set errors_count 0 ;# Number of errors occured while parsing ihex file
+ set eof 0 ;# Bool: EOF detected
+ set error_string {} ;# Text of error message
+
+ # Clear current data
+ if {$type != {code} && $type != {uni}} {
+ if {$type == {xdata}} {
+ for {set i 0} {$i < $capacity} {incr i} {
+ $project setXdataDEC $i 0
+ }
+ } elseif {$type == {eram}} {
+ for {set i 0} {$i < $capacity} {incr i} {
+ $project setEramDEC $i 0
+ }
+ }
+ $project rightPanel_watch_sync_all
+ }
+
+ # Iterate over data lines
+ foreach line $hex_data {
+ incr line_number
+
+ # Skip comments
+ if {[string index $line 0] != {:}} {continue}
+
+ # Check for allowed characters
+ if {![regexp {^:[0-9A-Fa-f]+$} $line]} {
+ incr errors_count
+ append error_string [mc "Line\t%s:\tInvalid characters\n"] $line_number
+ continue
+ }
+
+ # Local variables
+ set check [string range $line {end-1} end] ;# Control count
+ set line [string range $line 1 {end-2}] ;# Whole line (just without Control count)
+ set data [string range $line 8 end] ;# Data
+ set len [string range $line 0 1] ;# Length of data
+ set addr [string range $line 2 5] ;# Address
+ set rectype [string range $line 6 7] ;# Record type
+
+ # Convert address and length to decimal
+ set addr [expr "0x$addr"]
+ set len [expr "0x$len"]
+
+ # Check for valid control count
+ if {$check != [::IHexTools::getCheckSum $line]} {
+ incr errors_count
+ append error_string [mc "Line\t%s:\tInvalid chceksum\n" $line_number]
+ continue
+ }
+ # Check for valid lenght
+ if {($len * 2) != [string bytelength $data]} {
+ incr errors_count
+ append error_string [mc "Line\t%s:\tInvalid length\n" $line_number]
+ continue
+ }
+ # Check for supported record types
+ if {$rectype == {01}} {
+ set eof 1
+ break
+ }
+ if {$rectype != {00}} {
+ incr errors_count
+ append error_string [mc "Line\t%s:\tUnknown record type: '%s'\n" $line_number $rectype]
+ continue
+ }
+
+ # Set current address
+ set pointer $addr
+ if {$pointer >= $capacity} {
+ break
+ }
+
+ # Parse data field
+ set len [expr {$len * 2}]
+ for {set i 0; set j 1} {$i < $len} {incr i 2; incr j 2} {
+ set number [string range $data $i $j]
+ if {$type == {uni}} {
+ $hexeditor setValue $pointer [expr "0x$number"]
+ } {
+ write_to_simulator $pointer [expr "0x$number"]
+ }
+ incr pointer
+ }
+ }
+
+ # Append error if there is no EOF
+ if {!$eof} {
+ incr errors_count
+ append error_string [mc "Line\t%s:\tMissing EOF" [expr {$line_number + 1}]]
+ }
+
+ # Invoke error dialog
+ if {$errors_count} {
+ # Create dialog window
+ set dialog [toplevel .error_message_dialog -bg {#EEEEEE}]
+
+ # Create main frame (text widget and scrolbar)
+ set main_frame [frame $dialog.main_frame]
+
+ # Create text widget
+ set text [text $main_frame.text \
+ -yscrollcommand "$main_frame.scrollbar set" \
+ -bg {#FFFFFF} -width 0 -height 0 \
+ ]
+ pack $text -side left -fill both -expand 1
+ # Create scrollbar
+ pack [ttk::scrollbar $main_frame.scrollbar \
+ -orient vertical \
+ -command "$text yview" \
+ ] -side right -fill y
+
+ # Pack main frame and create button "Close"
+ pack $main_frame -fill both -expand 1
+ pack [ttk::button $dialog.ok_button \
+ -text [mc "Close"] \
+ -command "
+ grab release $dialog
+ destroy $dialog
+ " \
+ ]
+
+ # Show error string and disable the text widget
+ $text insert end $error_string
+ $text configure -state disabled
+
+ # Set window attributes
+ wm iconphoto $dialog ::ICONS::16::no
+ wm title $dialog [mc "Error(s) occured while parsing IHEX file"]
+ wm minsize $dialog 500 250
+ wm protocol $dialog WM_DELETE_WINDOW "grab release $dialog; destroy $dialog"
+ wm transient $dialog $win
+ grab $dialog
+ raise $dialog
+ tkwait window $dialog
+ }
+ }
+
+ ## Synchronize all EntryBoxes with the given value
+ # @parm String exclude - Name entry to exclude from synchronization {dec hex oct bin}
+ # @parm String valtype - Value type (one of {val addr}) (Value | Address)
+ # @parm Int value - Value (must be in decimal)
+ # @return void
+ private method fill_entries {exclude valtype value} {
+ # Determinate maximum value length (number of digits)
+ if {$valtype == {val}} {
+ set hexlen 2
+ set octlen 3
+ set binlen 8
+ } {
+ set hexlen 4
+ set octlen 7
+ set binlen 16
+ }
+
+ # Empty value -> clear entry boxes
+ if {$value == {}} {
+ set hex {}
+ set oct {}
+ set bin {}
+ # Non empty value -> convert
+ } {
+ # To hexadecimal
+ set hex [format %X $value]
+ set len [string length $hex]
+ if {$len != $hexlen} {
+ set hex "[string repeat {0} [expr {$hexlen - $len}]]$hex"
+ }
+
+ # To octal
+ set oct [format %o $value]
+ set len [string length $oct]
+ if {$len != $octlen} {
+ set oct "[string repeat {0} [expr {$octlen - $len}]]$oct"
+ }
+
+ # To binary
+ set bin [NumSystem::dec2bin $value]
+ set len [string length $bin]
+ if {$len < $binlen} {
+ set bin "[string repeat {0} [expr {$binlen - $len}]]$bin"
+ }
+ }
+
+ # Synchronize EntryBoxes
+ if {$valtype == {val}} {
+ if {$exclude != {dec}} {
+ set ::HexEditDlg::dec_val_${obj_idx} $value
+ }
+ if {$exclude != {hex}} {
+ set ::HexEditDlg::hex_val_${obj_idx} $hex
+ }
+ if {$exclude != {oct}} {
+ set ::HexEditDlg::oct_val_${obj_idx} $oct
+ }
+ if {$exclude != {bin}} {
+ set ::HexEditDlg::bin_val_${obj_idx} $bin
+ }
+ } else {
+ if {$exclude != {dec}} {
+ set ::HexEditDlg::dec_addr_${obj_idx} $value
+ }
+ if {$exclude != {hex}} {
+ set ::HexEditDlg::hex_addr_${obj_idx} $hex
+ }
+ if {$exclude != {oct}} {
+ set ::HexEditDlg::oct_addr_${obj_idx} $oct
+ }
+ if {$exclude != {bin}} {
+ set ::HexEditDlg::bin_addr_${obj_idx} $bin
+ }
+ }
+ }
+
+ ## Change content of cursor address display on status bar
+ # @parm Mixed args - [lindex $args 0] == Decimal address
+ # @return void
+ public method change_right_stat_bar_addr args {
+ set address [lindex $args 0]
+
+ # Empty address
+ if {$address == {}} {
+ $right_sbar_label configure -text " --- "
+ return
+ }
+
+ # Non empty address -> convert to HEX and display
+ set address [format %X $address]
+ set len [string length $address]
+ if {$len < 4} {
+ set address "[string repeat {0} [expr {4 - $len}]]$address"
+ }
+ $right_sbar_label configure -text "0x$address"
+ }
+
+ ## Adjust view mode to content of mode combobox
+ # @return void
+ public method switch_mode {} {
+ set mode [lindex {hex dec oct} [$mode_combo_box current]]
+ set ::HexEditDlg::mode_${obj_idx} $mode
+ sbar_show {Working ...}
+ update
+ $hexeditor switch_mode $mode
+ sbar_show {}
+ }
+
+ ## This method should be called after value change in hexeditor
+ # This method writes new value to simulator engine, watchers and EntryBoxes
+ # @parm Int addr - Address of changed cell
+ # @parm int val - New value of the entry
+ # @return void
+ public method cell_value_changed {addr val} {
+ set current_cell $addr
+ set validation_ena 0
+ fill_entries {} val $val
+ write_to_simulator $addr $val
+ setModified 1
+ set validation_ena 1
+ }
+
+ ## This method should be called after current cell change in hexeditor
+ # Synchronizes EntryBoxes
+ # @parm Int addr - New cell address
+ # @return void
+ public method current_cell_changed {addr} {
+ set validation_ena 0
+ set current_cell $addr
+ set value [$hexeditor get_values $addr $addr]
+ fill_entries {} val $value
+ fill_entries {} addr $addr
+ set validation_ena 1
+ }
+
+ ## Validate content of EntryBox in bottom frame
+ # + Synchronize value with all others (if valid)
+ # @parm String valtype - Value type (Address {addr} | Value {val})
+ # @parm String radix - Number base (one of {oct hex dec bin})
+ # @parm String value - Value to validate (and synchronize)
+ # @return void
+ public method validate_entry {valtype radix value} {
+ # If validation is disabled or value is empty -> abort
+ if {!$validation_ena || $value == {}} {
+ return 1
+ }
+
+ # Check for valid characters
+ if {$valtype == {val}} {
+ set m 1
+ } {
+ set m 2
+ }
+ set len [string length $value]
+ switch -- $radix {
+ {dec} {
+ if {$len > (3 * $m) || ![string is digit $value]} {
+ return 0
+ }
+ }
+ {hex} {
+ if {$len > (2 * $m) || ![string is xdigit $value]} {
+ return 0
+ }
+ }
+ {oct} {
+ if {!$len > (3 * $m) || [regexp {^[0-7]+$} $value]} {
+ return 0
+ }
+ }
+ {bin} {
+ if {$len > (8 * $m) || ![regexp {^[01]+$} $value]} {
+ return 0
+ }
+ }
+ }
+
+ # Tempotary disable validations (to prevent infinite event loops)
+ set validation_ena 0
+
+ # Convert value to decimal
+ set value [string trimleft $value 0]
+ if {$value == {}} {
+ set value 0
+ }
+ switch -- $radix {
+ {hex} {
+ set value [expr "0x$value"]
+ }
+ {oct} {
+ set value [expr "0$value"]
+ }
+ {bin} {
+ set value [NumSystem::bin2dec $value]
+ }
+ }
+
+ # Check for allowed value range
+ if {$valtype == {val}} {
+ if {$value > 255} {
+ set validation_ena 1
+ return 0
+ }
+ } {
+ if {$value >= $capacity} {
+ set validation_ena 1
+ return 0
+ }
+ }
+
+ # Synchronize with all other
+ fill_entries $radix $valtype $value
+ if {$valtype == {val}} {
+ $hexeditor setValue $current_cell $value
+ write_to_simulator $current_cell $value
+ } {
+ set current_cell $value
+ $hexeditor setCurrentCell $value
+ }
+
+ # Set flag modified + Reenable validations
+ setModified 1
+ set validation_ena 1
+
+ return 1
+ }
+
+ ## Perform program jump
+ # @return void
+ public method prog_jump {} {
+ if {$type != {code}} {return}
+ $project setPC [subst "\$::HexEditDlg::dec_addr_${obj_idx}"]
+ set lineNum [$project simulator_getCurrentLine]
+ if {$lineNum != {}} {
+ $project move_simulator_line $lineNum
+ } {
+ $project editor_procedure {} unset_simulator_line {}
+ }
+ $project Simulator_sync_PC_etc
+ }
+
+ ## Perform subprogram call
+ # @return void
+ public method sub_call {} {
+ if {$type != {code}} {return}
+ $project simulator_subprog_call [subst "\$::HexEditDlg::dec_addr_${obj_idx}"]
+ set lineNum [$project simulator_getCurrentLine]
+ if {$lineNum != {}} {
+ $project move_simulator_line $lineNum
+ } {
+ $project editor_procedure {} unset_simulator_line {}
+ }
+ $project Simulator_sync_PC_etc
+ }
+
+ ## Adjust view mode to state of mode menu
+ # @return void
+ public method adjust_mode {} {
+ $mode_combo_box current [lsearch {hex dec oct} [subst "\$::HexEditDlg::mode_${obj_idx}"]]
+ sbar_show {Working ...}
+ update
+ $hexeditor switch_mode [subst "\$::HexEditDlg::mode_${obj_idx}"]
+ sbar_show {Working}
+ }
+
+ ## Quit dialog
+ # @return void
+ public method quit {} {
+ if {$modified} {
+ set response [tk_messageBox \
+ -parent $win \
+ -type yesnocancel \
+ -icon question \
+ -title [mc "File modified"] \
+ -message [mc "File %s has been modifed.\nDo you want to save it ?" [file tail $opened_file]]]
+ if {$response == {yes}} {
+ save
+ } elseif {$response != {no}} {
+ return
+ }
+ }
+ if {$type == {uni}} {
+ delete object $this
+ } {
+ ::X::close_hexedit $type $project
+ }
+ }
+
+ ## Show status tip for menu entry
+ # @parm Int help_file_index - Menu index
+ # @parm Int entry_index - Entry index
+ # @return void
+ public method menu_sbar_show {help_file_index entry_index} {
+ # Validate input data
+ if {![string is digit $entry_index]} {
+ $left_sbar_label configure -text {}
+ return
+ }
+ if {![string is digit $help_file_index]} {
+ $left_sbar_label configure -text {}
+ return
+ }
+
+ # Show status tip
+ if {$type == {code}} {
+ $left_sbar_label configure -text \
+ [mc [lindex $HELPFILE_CODE [list $help_file_index $entry_index]]]
+ } {
+ $left_sbar_label configure -text \
+ [mc [lindex $HELPFILE_XDATA [list $help_file_index $entry_index]]]
+ }
+ }
+
+ ## Load data from simulator engine to current visible area
+ # @return void
+ public method load_data_to_current_view {} {
+ if {$type == {uni}} {return}
+
+ # Local variables
+ set startrow [$hexeditor getTopRow] ;# Start row
+ set endrow [expr {$startrow + 15}] ;# End row
+ set startaddr [expr {$startrow * 16}] ;# Start address for 1st row
+ set endaddr [expr {($startrow + 1) * 16 - 1}] ;# End address for 1st row
+
+ # Determinate command to gain data
+ if {$type == {code}} {
+ set cmd {getCodeDEC}
+ } elseif {$type == {xdata}} {
+ set cmd {getXdataDEC}
+ } elseif {$type == {eeprom}} {
+ set cmd {getEepromDEC}
+ } else {
+ set cmd {getEramDEC}
+ }
+
+ # Iterate over visible rows and load data to them
+ for {set row $startrow} {$row <= $endrow} {incr row} {
+ if {[string index $loaded_lines $row] == 1} {
+ incr startaddr 16
+ incr endaddr 16
+ continue
+ }
+
+ for {set addr $startaddr} {$addr <= $endaddr} {incr addr} {
+ if {$addr >= $capacity} {
+ break
+ }
+ $hexeditor setValue $addr [$project $cmd $addr]
+ }
+
+ incr startaddr 16
+ incr endaddr 16
+ }
+
+ # Adjust map of loaded lines
+ set loaded_lines [string replace $loaded_lines $startrow $endrow [string repeat 1 16]]
+ }
+
+ ## Show text in status bar
+ # @parm String text - Text to show
+ # @return void
+ public method sbar_show {text} {
+ $left_sbar_label configure -text $text
+ }
+
+ ## Action for Menu/Toolbar - Copy
+ # Invoke dialog "Find string"
+ # @return void
+ public method text_copy {} {
+ $hexeditor text_copy
+ }
+
+ ## Action for Menu/Toolbar - Paste
+ # Invoke dialog "Find string"
+ # @return void
+ public method text_paste {} {
+ $hexeditor text_paste
+ }
+
+ ## Action for Menu/Toolbar - "Find" or "Find next" or "Find previous"
+ # Invoke dialog "Find string"
+ # @return void
+ public method find_string {action} {
+ switch -- $action {
+ 0 {$hexeditor find_dialog}
+ 1 {$hexeditor find_next}
+ 2 {$hexeditor find_prev}
+ }
+ }
+
+ ## Action for Menu/Toolbar - Reload
+ # Reload content of HexEditor
+ # @return void
+ public method reload {} {
+ # Store original cursor position
+ set current_cursor_pos [$hexeditor getCurrentCell]
+
+ if {$type != {xdata} && $type != {uni}} {return}
+ if {$type == {uni}} {
+ set ext [string replace [file extension $opened_file] 0 0]
+ if {$ext != {} && $opened_file != {}} {
+ open_file $opened_file $ext
+ }
+ } {
+ refresh
+ }
+
+ # Restore original cursor position
+ update
+ $hexeditor setCurrentCell $current_cursor_pos
+ $hexeditor seeCell $current_cursor_pos
+ }
+
+ ## Action for Menu/Toolbar - Save as
+ # Save current content of hexeditor as IHEX8 file and ask for file name
+ # @return void
+ public method saveas {} {
+ set directory [file dirname $opened_file]
+ if {$type == {uni}} {
+ if {${::X::project_menu_locked}} {
+ set project {}
+ } {
+ set project ${::X::actualProject}
+ }
+ }
+ if {$directory == {.}} {
+ if {$project == {}} {
+ set directory ${::X::defaultDirectory}
+ } {
+ set directory [$project cget -projectPath]
+ }
+ }
+ catch {delete object fsd}
+ KIFSD::FSD fsd \
+ -title [mc "Save file - MCU 8051 IDE"] \
+ -master $win \
+ -directory $directory \
+ -initialfile [$middle_sbar_label cget -text] \
+ -defaultmask 0 -multiple 0 -filetypes {
+ {{IHEX8} {*.{hex,ihx}} }
+ {{All files} {*} }
+ }
+ fsd setokcmd "$this save_file_proc \[::HexEditDlg::fsd get\]"
+ fsd activate
+ }
+
+ ## Action for Menu/Toolbar - Save
+ # Save current content of the editor to $opened_file
+ # @return void
+ public method save {} {
+ if {$opened_file == {}} {
+ saveas
+ return
+ }
+ save_file_proc $opened_file
+ }
+
+ ## Action for Menu/Toolbar - Open Hex
+ # @return void
+ public method openhex {} {
+ set directory [file dirname $opened_file]
+ if {$type == {uni}} {
+ if {${::X::project_menu_locked}} {
+ set project {}
+ } {
+ set project ${::X::actualProject}
+ }
+ }
+ if {$directory == {.}} {
+ if {$project == {}} {
+ set directory ${::X::defaultDirectory}
+ } {
+ set directory [$project cget -projectPath]
+ }
+ }
+ catch {delete object fsd}
+ KIFSD::FSD fsd \
+ -title [mc "Open file - MCU 8051 IDE"] \
+ -master $win -directory $directory \
+ -defaultmask 0 -multiple 0 -filetypes {
+ {{IHEX8} {*.{hex,ihx}} }
+ {{All files} {*} }
+ }
+ fsd setokcmd "$this open_file \[::HexEditDlg::fsd get\] hex"
+ fsd activate
+ }
+
+ ## Action for Menu/Toolbar - Open Adb
+ # @return void
+ public method opensim {} {
+ if {$type != {code}} {return}
+ set directory [file dirname $opened_file]
+ if {$directory == {.}} {
+ set directory [$project cget -projectPath]
+ }
+ catch {delete object fsd}
+ KIFSD::FSD fsd \
+ -title [mc "Open file - MCU 8051 IDE"] \
+ -master $win -directory $directory \
+ -defaultmask 0 -multiple 0 -filetypes {
+ {{Simulator file} {*.adb} }
+ {{All files} {*} }
+ }
+ fsd setokcmd "$this open_file \[::HexEditDlg::fsd get\] adf"
+ fsd activate
+ }
+
+ ## Open the give file and load its contents into editor
+ # @parm String filename - Relative or absolute filename
+ # @parm String extension - Fily type {adf hex}
+ # @return void
+ public method open_file {filename extension} {
+ # Store original cursor position
+ set current_cursor_pos [$hexeditor getCurrentCell]
+
+ # Normalize filename
+ set filename [file normalize $filename]
+ set directory [file dirname $filename]
+ if {$type == {uni}} {
+ if {${::X::project_menu_locked}} {
+ set project {}
+ } {
+ set project ${::X::actualProject}
+ }
+ }
+ if {$directory == {.}} {
+ if {$project == {}} {
+ set directory ${::X::defaultDirectory}
+ } {
+ set directory [$project cget -projectPath]
+ }
+ }
+ if {!$::MICROSOFT_WINDOWS} { ;# POSIX way
+ if {![regexp "^(~|/)" $filename]} {
+ set filename "$directory/$filename"
+ }
+ } { ;# Microsoft windows way
+ if {![regexp "^\w:" $filename]} {
+ set filename [file join $directory $filename]
+ }
+ }
+
+ # Open file
+ if {[catch {
+ set file [open $filename r]
+ }]} then {
+ tk_messageBox \
+ -parent $win \
+ -type ok \
+ -icon warning \
+ -title [mc "Permission denied"] \
+ -message [mc "Unable to open file:\n%s" $filename]
+ return
+ }
+
+ # Clear editor
+ if {$type == {uni}} {
+ $hexeditor fill_views
+ } {
+ $project simulator_clear_memory $type
+ }
+
+ # Load contents
+ if {$extension == {adf}} {
+ $project load_program_from_adf $filename
+ } {
+ readHex [read $file]
+ }
+
+ # Finalize
+ close $file
+ set_filename $filename
+ refresh
+
+ # Restore original cursor position
+ update
+ $hexeditor setCurrentCell $current_cursor_pos
+ $hexeditor seeCell $current_cursor_pos
+ }
+
+ ## Save content of the editor into the given file in format IHEX8
+ # @parm String filename - target filename
+ # @return void
+ public method save_file_proc {filename} {
+ # Adjust filename
+ set filename [file normalize $filename]
+ set directory [file dirname $filename]
+ set rootname $filename
+ if {$type == {uni}} {
+ if {${::X::project_menu_locked}} {
+ set project {}
+ } {
+ set project ${::X::actualProject}
+ }
+ }
+ if {$directory == {.}} {
+ if {$project == {}} {
+ set directory ${::X::defaultDirectory}
+ } {
+ set directory [$project cget -projectPath]
+ }
+ }
+ if {!$::MICROSOFT_WINDOWS} { ;# POSIX way
+ if {![regexp "^(~|/)" $filename]} {
+ set filename "$directory/$filename"
+ }
+ } { ;# Microsoft windows way
+ if {![regexp "^\w:" $filename]} {
+ set filename [file join $directory $filename]
+ }
+ }
+
+ # Adjust file extension
+ if {![regexp {\.(hex|ihx)$} $filename]} {
+ if {$type != {code} && $type != {uni} } {
+ append filename {.xdata.hex}
+ } {
+ append filename {.hex}
+ }
+ }
+
+ if {[file exists $filename]} {
+ # Check if the file is writable
+ if {![file writable $filename]} {
+ tk_messageBox -type ok -icon error -title [mc "Permission denied"] \
+ -message [mc "Unable to access file: %s" $filename] -parent $win
+ return
+ }
+ # Ask user for overwrite existing file
+ if {[tk_messageBox \
+ -type yesno \
+ -icon question \
+ -parent $win \
+ -title [mc "Overwrite file"] \
+ -message [mc "A file name '%s' already exists. Are you sure you want to overwrite it ?" [file tail $filename]]
+ ] != {yes}
+ } {
+ return
+ }
+ # Create a backup file
+ catch {
+ file rename -force $filename "$filename~"
+ }
+ }
+
+ # Write filename on statusbar
+ set_filename $filename
+
+ if {$type == {xdata}} {
+ set getDataCommand {getXdata}
+
+ } elseif {$type == {eram}} {
+ set getDataCommand {getEram}
+
+ } elseif {$type == {eeprom}} {
+ set getDataCommand {getEeprom}
+
+ } else {
+ set getDataCommand {getCode}
+ }
+
+ # Open file
+ if {[catch {
+ set file [open $filename w 420]
+ }]} {
+ tk_messageBox \
+ -parent $win \
+ -type ok \
+ -icon warning \
+ -title [mc "Permission denied"] \
+ -message [mc "Unable to open file:\n%s" $filename]
+ return
+ }
+
+ # Determinate number of 2048 B blocks
+ set maximum [expr {$capacity / 2048 + 1}]
+
+ # Create progress dialog
+ set ::X::saving_progress 0
+ set ::X::abort_saving 0
+
+ create_progress_bar .prgDl \
+ $win \
+ {} \
+ "Saving: $rootname" \
+ ::X::saving_progress \
+ $maximum \
+ [mc "Saving file"] \
+ ::ICONS::16::filesave \
+ [mc "Abort"] \
+ {set ::X::abort_saving 1}
+
+ # Local variables
+ set addr 0 ;# Address
+ set len 0 ;# Length
+ set data {} ;# Data field
+ set pointer -1 ;# Current address
+
+ # $maximum* update Progress Dialog
+ for {set i 0} {$i < $maximum} {incr i} {
+
+ # Create 8*32 IHEX records
+ for {set j 0} {$j < 2048} {incr j} {
+
+ # Increment address pointer
+ incr pointer
+ if {$pointer >= $capacity} {
+ break
+ }
+
+ # Get register value
+ set code [$hexeditor get_values $pointer $pointer]
+ if {$code != {}} {
+ set code [format {%X} $code]
+ if {[string length $code] == 1} {
+ set code "0$code"
+ }
+ }
+
+ # If buffer is full -> create record
+ if {$code == {} || $len == 255} {
+ # Save record
+ if {$len != 0} {
+ puts -nonewline $file {:}
+ puts $file [createHexRecord \
+ [format "%X" $len] \
+ [format "%X" $addr] \
+ 00 $data \
+ ]
+ }
+ # Reset some variables related to the last record
+ set addr $pointer
+ incr addr
+ set len 0
+ set data {}
+ if {$len != 255} {continue}
+ }
+
+ # Increment length field and append register value to data field
+ incr len
+ append data $code
+ }
+
+ # Update Progress Dialog
+ incr ::X::saving_progress
+ update
+ # Optionaly abort
+ if {${::X::abort_saving}} {
+ set abort_saving 0
+ destroy .prgDl
+ return
+ }
+ }
+
+ # Destroy Progress Dialog
+ catch {destroy .prgDl}
+
+ # Save the last (incomplete) record
+ if {$len != 0} {
+ puts -nonewline $file {:}
+ puts $file [::HexEditDlg::createHexRecord \
+ [format "%X" $len] [format "%X" $addr] 00 $data]
+ }
+
+ # Save EOF
+ puts -nonewline $file {:00000001FF}
+
+ # Done ...
+ close $file
+ setModified 0
+ sbar_show [mc "File %s saved" $filename]
+ }
+
+ ## Create Intel HEX 8 field
+ # @parm String len - field length (max. 2 hex digits)
+ # @parm String addr - field address (max. 4 hex digits)
+ # @parm String type - field type (exaclty 2 hex digits (eg. '00' or '01'))
+ # @parm String data - data (even number of hex digits, max. 512)
+ # @return String - Intel HEX 8 field
+ proc createHexRecord {len addr rectype data} {
+ # Adjust length
+ if {[string length $len] == 1} {set len "0$len"}
+ # Adjust address
+ set addr_len [string length $addr]
+ if {$addr_len < 4} {
+ set addr "[string repeat 0 [expr {4 - $addr_len}]]$addr"
+ }
+ # Create field
+ set result "${len}${addr}${rectype}"
+ append result $data
+ # Compute control count (see Compiler)
+ append result [::IHexTools::getCheckSum $result]
+ # Return result
+ return $result
+ }
+
+ # -------------------------------------------------------------------
+ # GENERAL PUBLIC INTERFACE
+ # -------------------------------------------------------------------
+
+ ## Inform hexeditor about simulator start or shutdown
+ # @parm Bool started - 1 == Simulator started; 0 == Simulator stopped
+ # @return void
+ public method simulator_stared_stopped {started} {
+ if {$type != {code}} {
+ return
+ }
+
+ if {$started} {
+ set state {normal}
+ } {
+ set state {disabled}
+ }
+ $sub_call_but configure -state $state
+ $prg_jump_but configure -state $state
+ [$hexeditor get_popup_menu] entryconfigure [::mc "LJMP this_address"] -state $state
+ [$hexeditor get_popup_menu] entryconfigure [::mc "LCALL this_address"] -state $state
+ }
+
+ ## Move program pointer (highlight cells) -- Avaliable only for code memory hexeditor
+ # @parm Int new_PC - New program counter
+ # @parm Int int_length - Instruction length
+ # @return void
+ public method move_program_pointer {new_PC int_length} {
+ if {$type != {code}} {
+ return
+ }
+
+ if {$pre_last_PC > -1} {
+ for {set i 0} {$i < $pre_last_PC_length} {incr i} {
+ $hexeditor set_bg_hg $pre_last_PC 0 1
+ incr pre_last_PC
+ }
+ }
+ set pre_last_PC $last_PC
+ set pre_last_PC_length $last_PC_length
+
+ if {$last_PC > -1} {
+ for {set i 0} {$i < $last_PC_length} {incr i} {
+ $hexeditor set_bg_hg $last_PC 1 1
+ $hexeditor set_bg_hg $last_PC 0 2
+ incr last_PC
+ }
+ }
+
+ set last_PC_length_d 0
+ set last_PC_d -1
+ set last_PC_length $int_length
+ set last_PC $new_PC
+
+ for {set i 0} {$i < $int_length} {incr i} {
+ $hexeditor set_bg_hg $new_PC 1 2
+ incr new_PC
+ }
+ $hexeditor seeCell $new_PC
+ }
+
+ ## Directly move program pointer (do not affect previous PC pointer) -- Avaliable only for code memory hexeditor
+ # @parm Int new_PC - New program counter (-1 == unresolved)
+ # @parm Int int_length - Instruction length
+ # @return void
+ public method move_program_pointer_directly {new_PC int_length} {
+ if {$type != {code}} {
+ return
+ }
+
+ if {$last_PC_d > -1} {
+ for {set i 0} {$i < $last_PC_length_d} {incr i} {
+ $hexeditor set_bg_hg $last_PC_d 0 0
+ incr last_PC_d
+ }
+ }
+ if {$new_PC == -1} {
+ set last_PC_length_d 0
+ set last_PC_d -1
+ return
+ }
+ set last_PC_length_d $int_length
+ set last_PC_d $new_PC
+
+ for {set i 0} {$i < $int_length} {incr i} {
+ $hexeditor set_bg_hg $new_PC 1 0
+ incr new_PC
+ }
+ $hexeditor seeCell $new_PC
+ }
+
+ ## Clear highlight for all cells in the editor
+ # @return void
+ public method clear_highlight {} {
+ $hexeditor clearHighlighting
+ }
+
+ ## Set background highlight
+ # @parm Int addr - Cell address
+ # @parm Bool bool - 1 == Set; 0 == Clear
+ # @return void
+ public method set_bg_hg_clr {addr bool} {
+ $hexeditor set_bg_hg $addr $bool 0
+ }
+
+ ## Write value to the editor
+ # - avaliable only in modes: XDATA and ERAM
+ # @parm String address - hexadecimal address
+ # @return void
+ public method reg_sync {address} {
+ if {$type == {code}} {
+ return
+ }
+
+ set address [expr "0x$address"]
+ if {$type == {xdata}} {
+ set val [$project getXdataDEC $address]
+ } elseif {$type == {eeprom}} {
+ set val [$project getEepromDEC $address]
+ } else {
+ set val [$project getEramDEC $address]
+ }
+ set org_val [$hexeditor get_values $address $address]
+ $hexeditor setValue $address $val
+ if {$org_val != $val} {
+ $hexeditor setHighlighted $address 1
+ }
+ if {$address == $current_cell} {
+ set validation_ena 0
+ fill_entries {} val $val
+ set validation_ena 1
+ }
+ setModified 1
+ }
+
+ ## Reload content of the editor
+ # @return void
+ public method refresh {} {
+ if {$type == {uni}} {return}
+
+ set loaded_lines [string repeat [string repeat 0 0xFF] 0xFF]
+ load_data_to_current_view
+ setModified 1
+ }
+
+ ## Get configuration list
+ # @return List - config list for procedure loadConfig
+ proc getConfig {} {
+ return [list $win_pos $mode $cell $current_view [::HexEditor::get_config]]
+ }
+
+ ## Load config list (result of procedure getConfig)
+ # @parm List - config list
+ # @return void
+ proc loadConfig {config} {
+ # Parse config list
+ set win_pos [lindex $config 0]
+ set mode [lindex $config 1]
+ set cell [lindex $config 2]
+ set current_view [lindex $config 3]
+
+ # load configuration for hexeditor widget
+ ::HexEditor::load_config_list [lindex $config 4]
+
+ # Validate loaded values
+ if {![regexp {^\+\d+\+\d+$} $win_pos]} {
+ puts stderr "Invalid value of key win_pos (`$win_pos')"
+ set win_pos {+0+0}
+ }
+ if {$mode != {hex} && $mode != {dec} && $mode != {oct}} {
+ puts stderr "Invalid value of key mode (`$mode')"
+ set mode {hex}
+ }
+ if {![string is digit -strict $cell]} {
+ puts stderr "Invalid value of key cell (`$cell')"
+ set cell 0
+ }
+ if {$current_view != {left} && $current_view != {right}} {
+ puts stderr "Invalid value of key current_view (`$current_view')"
+ set current_view {left}
+ }
+ }
+
+ ## Set name of current file (for purpose of saving and for status bar)
+ # @parm String filename - Full filename
+ # @return void
+ public method set_filename {filename} {
+ set opened_file $filename
+
+ set filename [file tail $filename]
+ $middle_sbar_label configure -text $filename -helptext $opened_file
+ if {$type == {uni}} {
+ if {$modified} {
+ wm title $win "\[modified\] $filename - [mc {Hexadecimal editor}] - MCU 8051 IDE"
+ } {
+ wm title $win "$filename - [mc {Hexadecimal editor}] - MCU 8051 IDE"
+ }
+ }
+ }
+}