diff options
Diffstat (limited to 'lib/lib/ihextools.tcl')
-rwxr-xr-x | lib/lib/ihextools.tcl | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/lib/lib/ihextools.tcl b/lib/lib/ihextools.tcl new file mode 100755 index 0000000..071b178 --- /dev/null +++ b/lib/lib/ihextools.tcl @@ -0,0 +1,523 @@ +#!/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 +# Provides some tools for manipulating IHEX8, binary and sim files. +# It's intented for converting between these file types and for +# normalizing hex files. +# -------------------------------------------------------------------------- + +namespace eval IHexTools { + + ## PUBLIC + variable update 0 ;# Bool: Periodicaly update GUI and increment progress + variable abort 0 ;# Bool: Abort currently running procedure + variable progress 1 ;# Int: Variable for progress bars + variable error_count 0 ;# Int: Count of errors + variable error_string {} ;# Error messages + variable highest_addr 0 ;# Int: Highest address in loaded IHEX file + + ## PRIVATE + variable content ;# Array: Currently loaded data (content(0..65535) => 00..FF) + variable INITIALIZED 0 ;# Bool: Namespace variables initialized + variable data_field ;# Auxiliary variable for creating IHEX records + variable data_field_len ;# Auxiliary variable for creating IHEX records + + + # ---------------------------------------------------------------- + # GENERAL PURPOSE PROCEDURES + # ---------------------------------------------------------------- + + ## Compute checksum for the given HEX field (without leading colon) + # @parm String hex_data - HEX field without leading colon + # @return String - resulting hexadecimal checksum + proc getCheckSum {hex_data} { + + set sum 256 ;# Initial checksum + set hex_data [split $hex_data {}] + + # Iterate over hex data + for {set i 0} {1} {incr i} { + + # Gain 1st hex digit + set val [lindex $hex_data $i] + + # If the 1st digit is empty -> return result + if {$val == {}} { + # Handle overflow + if {$sum == 256} {return {00}} + # Convert decimal checksum to hexadecimal + set sum [format "%X" $sum] + if {[string length $sum] == 1} { + set sum "0$sum" + } + return $sum + } + + # Gain 2nd hex digit + incr i + append val [lindex $hex_data $i] + set val [expr "0x$val"] + + # Decrement checksum + incr sum -$val + + # Handle undeflow + if {$sum < 0} {incr sum 256} + } + } + + ## Get maximum value for progressbar when loading hex or sim file + # @parm String data - input sim or hex data + # @return Int - number of iterations divided by 25 + proc get_number_of_iterations {data} { + # Any EOL to LF + regsub -all {\r\n?} $data "\n" data + + # Local variables + set result 0 ;# Resulting number + set index 0 ;# Last search result + + # Get number of LF chracters + while 1 { + set index [string first "\n" $data $index] + if {$index == -1} {break} + incr index + } + + # Return result + return [expr {$result / 25 + 1}] + } + + ## Load IHEX 8 file into internal memory + # @parm String hex_data - Content of IHEX8 file to load + # @return Bool - result + proc load_hex_data {hex_data} { + variable INITIALIZED ;# Bool: Namespace variables initialized + + variable content ;# Array: Currently loaded data + variable update ;# Bool: Periodicaly update GUI and increment progress + variable abort 0 ;# Bool: Abort currently running procedure + variable progress 1 ;# Int: Variable for progress bars + variable error_count 0 ;# Int: Count of errors + variable error_string {} ;# Error messages + variable highest_addr 0 ;# Int: Highest address in loaded IHEX file + + # Initialize array of loaded data + if {!$INITIALIZED} { + free_resources + } + # Convert any EOL to LF + regsub -all {\r\n?} $hex_data "\n" hex_data + + # Local variables + set lineNum 0 ;# Number of the current line + set eof 0 ;# Bool: EOF detected + + # Iterate over HEX records + foreach line [split $hex_data "\n"] { + incr lineNum ;# Increment line number + + # Skip comments + if {[string index $line 0] != {:}} {continue} + + # Check for valid charters + if {![regexp {^:[0-9A-Fa-f]+$} $line]} { + Error $lineNum [mc "Line contains invalid characters"] + continue + } + # Check for odd lenght + set len [string length $line] + if {[expr {$len % 2}] != 1} { + Error $lineNum [mc "Line contains even number of characters"] + continue + } + + # Analize HEX record + set len [ string range $line 1 2 ] ;# Lenght field + set addr [ string range $line 3 6 ] ;# Address field + set type [ string range $line 7 8 ] ;# Type field + set data [ string range $line 9 {end-2} ] ;# Data field + set check [ string range $line {end-1} end ] ;# Checksum field + set line [ string range $line 1 {end-2} ] ;# Record without ':' and checksum + + # Handle record type (01 == EOF; 00 == normal record) + if {$type == {01}} { + set eof 1 + break + } elseif {$type != {00}} { + Error $lineNum [mc "Unknown record type '%s'" $type] + continue + } + + # Check for valid checksum + set new_check [getCheckSum $line] + if {$new_check != $check} { + Error $lineNum [mc "Bad checksum"] + continue + } + + # Check for correct value of the length field + set len [expr "0x$len"] + if {([string length $data] / 2) != $len} { + Error $lineNum [mc "Bad length"] + continue + } + + # Parse and load data field + set addr [expr "0x$addr"] + for {set i 0; set j 1} {$i < ($len * 2)} {incr i 2; incr j 2} { + set content($addr) [string range $data $i $j] + incr addr + } + + # Store highest address + if {$addr > $highest_addr} { + set highest_addr $addr + } + + # Update GUI and progress variable + if {$update} { + if {![expr {$lineNum % 25}]} { + # Conditional abort + if {$abort} {return 0} + # Update progress variable and GUI + incr progress + update + } + } + } + + # If there is no EOF then report that as an error + if {!$eof} { + Error - [mc "Missing EOF"] + } + + # Return result + if {$error_count} { + return 0 + } { + return 1 + } + } + + ## Load binary file into internal memory + # @parm String data - Binary data to load + # @return Bool - result + proc load_bin_data {data} { + variable INITIALIZED ;# Bool: Namespace variables initialized + + variable content ;# Array: Currently loaded data + variable update ;# Bool: Periodicaly update GUI and increment progress + variable abort 0 ;# Bool: Abort currently running procedure + variable progress 1 ;# Int: Variable for progress bars + variable error_count 0 ;# Int: Count of errors + variable error_string {} ;# Error messages + + # Initialize array of loaded data + if {!$INITIALIZED} { + free_resources + } + + # Check for allowed data length + set len [string length $data] + if {$len > 0x10000} { + Error - [mc "Data length exceeding limit 0x10000"] + return 0 + } + + # Load data + set val 0 + for {set i 0} {$i < $len} {incr i} { + binary scan [string index $data $i] c val ;# bin -> dec + set content($i) [string range [format %X $val] end-1 end] ;# dec -> hex + } + return 1 + } + + ## Load simulator file into internal memory + # @parm String data - Content of simulator file to load + # @return Bool - result + proc load_sim_data {data} { + variable INITIALIZED ;# Bool: Namespace variables initialized + + variable content ;# Array: Currently loaded data + variable update ;# Bool: Periodicaly update GUI and increment progress + variable abort 0 ;# Bool: Abort currently running procedure + variable progress 1 ;# Int: Variable for progress bars + variable error_count 0 ;# Int: Count of errors + variable error_string {} ;# Error messages + + # Initialize array of loaded data + if {!$INITIALIZED} { + free_resources + } + + # Adjust input data + regsub -all {\r\n?} $data "\n" data ;# Any EOL to LF + regsub -all -line {\s*#.*$} $data {} data ;# Remove comments + regsub {^[^\n]+\n} $data {} data ;# Discard the first line + + set lineNum 0 ;# Line number + + # Iterate over lines in the given data + foreach line [split $data "\n"] { + incr lineNum ;# Increment line number + + # Skip empty lines + if {$line == {}} {continue} + + # Anylize line + set ln [lindex $line 0] ;# Line number + set addr [lindex $line 1] ;# Address + set line [lreplace $line 0 1] ;# Processor codes + + # Check for validity of line number + if {![string is digit -strict $ln]} { + Error $lineNum [mc "Invalid line number '%s'" $ln] + continue + } + # Check for validity of address + if {![string is digit -strict $addr]} { + Error $lineNum [mc "Invalid address '%s'" $addr] + continue + } + # Check for allowed characters + if {![regexp {^[\d \t]+$} $line] || ![llength $line]} { + Error $lineNum [mc "Invalid data field"] + continue + } + + # Load processor codes + foreach val $line { + set content($addr) [format %X $val] + incr addr + } + + # Update GUI and progress variable + if {$update} { + if {![expr {$lineNum % 25}]} { + # Conditional abort + if {$abort} {return 0} + # Update progress variable and GUI + incr progress + update + } + } + } + + # Return result + if {$error_count} { + return 0 + } { + return 1 + } + } + + ## Get loaded data as binary string + # @return String - Resulting binary data + proc get_bin_data {} { + variable content ;# Array: Currently loaded data + variable update ;# Bool: Periodicaly update GUI and increment progress + variable abort 0 ;# Bool: Abort currently running procedure + variable progress 1 ;# Int: Variable for progress bars + + # Local variables + set addr 0 ;# Current address + set pad {} ;# Padding + set result {} ;# Resulting binary string + + # Load data and convert them (16 x 4096 interations) + for {set j 0} {$j < 16} {incr j} { + for {set i 0} {$i < 4096} {incr i} { + # Get hexadecimal value + set hex $content($addr) + # Convert it to binary value + if {$hex == {}} { + append pad "\0" + } { + if {$pad != {}} { + append result $pad + set pad {} + } + append result [subst "\\x$hex"] + } + # Increment address + incr addr + } + + # Update GUI and progress variable + if {$update} { + # Update progress variable and GUI + incr progress + update + # Conditional abort + if {$abort} { + return {} + } + } + } + + # Return resulting binary string + return $result + } + + ## Get loaded data as IHEX8 + # @return String - Resulting IHEX8 + proc get_hex_data {} { + variable content ;# Array: Currently loaded data + variable update ;# Bool: Periodicaly update GUI and increment progress + variable abort 0 ;# Bool: Abort currently running procedure + variable progress 1 ;# Int: Variable for progress bars + variable data_field ;# Auxiliary variable for creating IHEX records + variable data_field_len ;# Auxiliary variable for creating IHEX records + + # Local variables + set pointer 0 ;# Current address + set data_field_len 0 ;# IHEX8 Data field lenght + set data_field {} ;# IHEX8 Data field + set result {} ;# Resulting IHEX8 + + # Load data (16 x 4096 interations) + for {set j 0} {$j < 16} {incr j} { + for {set i 0} {$i < 4096} {incr i} { + # Determinate HEX value + set hex $content($pointer) + + # If HEX value if empty -> write record + if {$hex == {} && $data_field_len} { + create_hex_record [expr {$pointer - $data_field_len}] + append result $data_field + set data_field {} + + # Append HEX value to the current data field + } elseif {$hex != {}} { + if {[string length $hex] == 1} { + set hex "0$hex" + } + + append data_field $hex + incr data_field_len + } + + # Increment current address + incr pointer + + # If data field length is high -> write record + if {$data_field_len == ${::Compiler::Settings::max_ihex_rec_length}} { + create_hex_record [expr {$pointer - $data_field_len}] + append result $data_field + set data_field {} + } + } + + # Update GUI and progress variable + if {$update} { + # Update progress variable and GUI + incr progress + update + # Conditional abort + if {$abort} { + return {} + } + } + } + + # Append EOF and return result + append result {:00000001FF} + return $result + } + + ## Free used resources + # @return void + proc free_resources {} { + variable content ;# Array: Currently loaded data + variable error_string {} ;# Error messages + variable data_field 0 ;# Auxiliary variable for creating IHEX records + + # Reset array of loaded data + for {set i 0} {$i < 0x10000} {incr i} { + set content($i) {} + } + } + + ## Get value of particular cell in the loaded array + # @parm Int addr - Must be 0..65535 + # @return Int - -1 == Not defined; 0..255 loaded value + proc get_value {addr} { + variable content ;# Array: Currently loaded data + + if {$addr < 0 || $addr > 0xFFFF} { + return -1 + } + set result $content($addr) + if {$result == {}} { + return -1 + } { + return $result + } + } + + + + # ---------------------------------------------------------------- + # INTERNAL AUXILIARY PROCEDURES + # ---------------------------------------------------------------- + + ## Create IHEX8 record (result -> data_field) + # @parm String addr - Content of address firld (decimal number) + # @return void + proc create_hex_record {addr} { + variable data_field ;# Auxiliary variable for creating IHEX records + variable data_field_len ;# Auxiliary variable for creating IHEX records + + # Adjust address + set addr [format %X $addr] + set len [string length $addr] + if {$len != 4} { + set addr "[string repeat 0 [expr {4 - $len}]]$addr" + } + # Adjust lenght + set len [format %X $data_field_len] + if {[string length $len] == 1} { + set len "0$len" + } + + # Create HEX field + set data_field ":${len}${addr}00${data_field}[getCheckSum ${len}${addr}00${data_field}]\n" + set data_field_len 0 + } + + ## Append error message to error_string + # @parm Int line - Number of line where the error occured + # @parm String - Error message + # @return void + proc Error {line string} { + variable error_count ;# Int: Count of errors + variable error_string ;# Error messages + + incr error_count + append error_string [mc "Error at line %s:\t" $line] $string "\n" + } +} |