summaryrefslogtreecommitdiff
path: root/lib/lib/ihextools.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lib/ihextools.tcl')
-rwxr-xr-xlib/lib/ihextools.tcl523
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"
+ }
+}