diff options
Diffstat (limited to 'lib/compiler/external_compiler.tcl')
-rwxr-xr-x | lib/compiler/external_compiler.tcl | 1076 |
1 files changed, 1076 insertions, 0 deletions
diff --git a/lib/compiler/external_compiler.tcl b/lib/compiler/external_compiler.tcl new file mode 100755 index 0000000..3cf581b --- /dev/null +++ b/lib/compiler/external_compiler.tcl @@ -0,0 +1,1076 @@ +#!/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 interface to external compilers +# -------------------------------------------------------------------------- + +namespace eval ExternalCompiler { + variable input_filename ;# String: Name of file to compile (without extension) + variable compiler_used ;# Int: Compiler ID (1 == ASEM-51; 2 == ASL; 3 == AS31, other values have no meaning) + variable project_dir ;# String: Project directory + variable working_dir ;# String: Compiler working directory + variable input_file_base;# String: Full file name of the primary source file + + ## Int: Preffered assembler + # 0 - Native MCU 8051 IDE assembler + # 1 - ASEM-51 + # 2 - ASL + # 3 - AS31 + variable selected_assembler 0 + variable selected_assembler_def 0 ;# Int: Default value for $selected_assembler + + # + ## External assembler configuration + # + + ## ASEM-51 + # Default ASEM-51 assembler configuration + variable assembler_ASEM51_config_def { + --omf-51 0 + --columns 0 + --verbose 1 + -i {} + custom {} + } + # Current ASEM-51 assembler configuration + variable assembler_ASEM51_config + # Default ASEM-51 additional configuration + variable assembler_ASEM51_addcfg_def { + adf 1 + } + # Current ASEM-51 additional configuration + variable assembler_ASEM51_addcfg + + ## ASL + # Array: ASL additional configuration + variable assembler_ASL_addcfg + # Default ASL assembler configuration + variable assembler_ASL_config_def { + -A 0 -a 0 + -C 0 -c 0 + -h 0 -I 1 + -L 1 -M 0 + -P 0 -n 0 + -quiet 0 -s 1 + -u 0 -U 0 + -w 0 -x 0 + + -r {} -i {} + -g {MAP} -cpu {8051} + custom {} + } + # Current ASL assembler configuration + variable assembler_ASL_config + # Default ASL additional configuration + variable assembler_ASL_addcfg_def { + ihex 1 + adf 1 + } + # Current ASL additional configuration + variable assembler_ASL_addcfg + + ## AS31 + # Array: AS31 additional configuration + variable assembler_AS31_addcfg + # Default AS31 assembler configuration + variable assembler_AS31_config_def { + -l 1 -F {hex} + -A {} custom {} + } + # Current AS31 assembler configuration + variable assembler_AS31_config + # Default AS31 additional configuration + variable assembler_AS31_addcfg_def { + adf 1 + } + # Current ASL additional configuration + variable assembler_AS31_addcfg + + ## SDCC + # Default SDCC boolean options + variable sdcc_bool_options_def { + --verbose 1 + -V 1 + -S 0 + --compile-only 0 + --preprocessonly 0 + --c1mode 0 + --print-search-dirs 0 + --use-stdout 0 + --nostdlib 0 + --nostdinc 0 + --less-pedantic 0 + --debug 1 + --cyclomatic 0 + --fdollars-in-identifiers 0 + --funsigned-char 0 + --xstack 0 + --int-long-reent 0 + --float-reent 0 + --main-return 0 + --xram-movc 0 + --profile 0 + --fommit-frame-pointer 0 + --all-callee-saves 0 + --stack-probe 0 + --parms-in-bank1 0 + --no-xinit-opt 0 + --no-c-code-in-asm 0 + --no-peep-comments 0 + --fverbose-asm 0 + --short-is-8bits 0 + --stack-auto 0 + --nooverlay 1 + --nogcse 0 + --nolabelopt 0 + --noinvariant 0 + --noinduction 1 + --nojtbound 0 + --noloopreverse 0 + --no-peep 0 + --no-reg-params 0 + --peep-asm 0 + --opt-code-speed 0 + --opt-code-size 0 + --out-fmt-ihx 0 + --out-fmt-s19 0 + } + # Current SDCC boolean options + variable sdcc_bool_options + # Default SDCC string options + variable sdcc_string_options_def { + model --model-small + standard --std-sdcc89 + stack --pack-iram + custom {} + } + # Current SDCC string options + variable sdcc_string_options + # Default SDCC optional string options + variable sdcc_optional_string_options_def { + --codeseg {} + --constseg {} + --lib-path {} + --xram-loc {} + --xstack-loc {} + --code-loc {} + --stack-loc {} + --data-loc {} + --stack-size {} + } + # Current SDCC optional string options + variable sdcc_optional_string_options + # Default semicolon separated optional string options + variable sdcc_scs_string_options_def { + -I {} + -l {} + -L {} + --disable-warning {} + } + # Current semicolon separated optional string options + variable sdcc_scs_string_options + + + ## Make backup copies for files with the given extensions and remove original files + # (input_filename.extension -> input_filename.extension~)* + # @parm List suffixes - List of file extensions (e.g. {asm c h}) + # @return void + proc backup_and_remove {suffixes} { + variable input_filename ;# String: Name of file to compile (without extension) + + foreach ext $suffixes { + catch { + file rename -force -- "$input_filename.$ext" "$input_filename.$ext~" + } + } + } + + ## Start SDCC (ANSI C compiler) + # @parm String work_dir - Current working directory + # @parm String input_file - C source file to compile + # @parm Int iram - Amount of internal data memory + # @parm Int xram - Amount of external data memory + # @parm Int code - Amount of overall program memory + # @return Int - Compiler PID + proc compile_C {work_dir input_file iram xram code} { + variable input_filename ;# String: Name of file to compile (without extension) + variable compiler_used ;# Int: Compiler ID (1 == ASEM-51; 2 == ASL; 3 == AS31, other values have no meaning) + + set compiler_used 0 + set input_filename [file rootname $input_file] + backup_and_remove {asm cdb ihx} + set sdcc_opts [determinate_sdcc_options] + if {${::PROGRAM_AVALIABLE(sdcc-sdcc)}} { + set sdcc_cmd {sdcc-sdcc} + } { + set sdcc_cmd {sdcc} + } + ::X::messages_text_append [::Compiler::msgc {S}][mc "\n\nStarting compiler ..."] + if {!$::MICROSOFT_WINDOWS} { + ::X::messages_text_append "\ncd \"$work_dir\"\n$sdcc_cmd -mmcs51 --iram-size $iram --xram-size $xram --code-size $code $sdcc_opts \"$input_file\"" + } { + ::X::messages_text_append "\ncd \"$work_dir\"\n$sdcc_cmd -mmcs51 --iram-size $iram --xram-size $xram --code-size $code --nooverlay --noinduction --verbose --debug -V --std-sdcc89 --model-small --pack-iram \"$input_file\"" + } + if {[catch { + cd $work_dir + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nError: Unable to change working directory to '%s'" $work_dir] + } + if {!$::MICROSOFT_WINDOWS} { ;# Normal way (POSIX) + return [exec -- /bin/sh -c "$sdcc_cmd -mmcs51 \ + --iram-size $iram \ + --xram-size $xram \ + --code-size $code \ + $sdcc_opts \"$input_file\"" |& \ + tclsh "${::LIB_DIRNAME}/external_command.tcl" \ + [tk appname] \ + {::ExternalCompiler::ext_compilation_complete 1}\ + ::X::compilation_message & \ + ] + } { ;# Microsoft Windows way + eval [subst -nocommands { + return [exec -- "${::LIB_DIRNAME}/sdcc.bat" \ + "${::LIB_DIRNAME}" \ + $iram \ + $xram \ + $code \ + "$input_file" \ + |& \ + "${::LIB_DIRNAME}/external_command.bat" \ + "${::LIB_DIRNAME}/external_command.tcl" \ + [tk appname] \ + {::ExternalCompiler::ext_compilation_complete 1} \ + ::X::compilation_message & \ + ] + }] + } + } + + ## Start AS31 (Assembler) + # @parm String work_dir - Current working directory + # @parm String input_file - Assembler source file to compile + # @parm String project_directory - Project directory (for debug file) + # @return Int - Compiler PID + proc as31_compile {work_dir input_file project_directory} { + variable project_dir ;# String: Project directory + variable compiler_used ;# Int: Compiler ID (1 == ASEM-51; 2 == ASL; 3 == AS31, other values have no meaning) + variable input_filename ;# String: Name of file to compile (without extension) + variable working_dir ;# String: Compiler working directory + variable input_file_base;# String: Full file name of the primary source file + global argv + + set compiler_used 3 + set project_dir $project_directory + set input_file_base [file normalize [file join $work_dir $input_file]] + set input_filename [file rootname $input_file_base] + set working_dir $work_dir + + set as31_options [determinate_as31_options] + ::X::messages_text_append [::Compiler::msgc {S}][mc "\n\nStarting compiler ..."] + ::X::messages_text_append "\ncd \"$work_dir\"\nas31 $as31_options \"$input_file\"" + if {[catch { + cd $work_dir + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nError: Unable to change working directory to '%s'" $work_dir] + } + backup_and_remove {adf hex lst} + + return [exec -- /bin/sh -c "as31 $as31_options \"$input_file\"" |& \ + tclsh "${::LIB_DIRNAME}/external_command.tcl" "[tk appname]" \ + ::ExternalCompiler::ext_compilation_complete ::X::compilation_message & \ + ] + } + + ## Start ASEM-51 (Assembler) + # @parm String work_dir - Current working directory + # @parm String input_file - Assembler source file to compile + # @parm String project_directory - Project directory (for debug file) + # @return Int - Compiler PID + proc asem51_compile {work_dir input_file project_directory} { + variable project_dir ;# String: Project directory + variable compiler_used ;# Int: Compiler ID (1 == ASEM-51; 2 == ASL; 3 == AS31, other values have no meaning) + variable input_filename ;# String: Name of file to compile (without extension) + variable working_dir ;# String: Compiler working directory + variable input_file_base;# String: Full file name of the primary source file + global argv + + set compiler_used 1 + set project_dir $project_directory + set input_file_base [file normalize [file join $work_dir $input_file]] + set input_filename [file rootname $input_file_base] + set working_dir $work_dir + + set asem51_options [determinate_asem51_options] + ::X::messages_text_append [::Compiler::msgc {S}][mc "\n\nStarting compiler ..."] + ::X::messages_text_append "\ncd \"$work_dir\"\nasem $asem51_options \"$input_file\"" + if {[catch { + cd $work_dir + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nError: Unable to change working directory to '%s'" $work_dir] + } + backup_and_remove {adf hex lst omf} + + return [exec -- /bin/sh -c "asem $asem51_options \"$input_file\"" |& \ + tclsh "${::LIB_DIRNAME}/external_command.tcl" "[tk appname]" \ + ::ExternalCompiler::ext_compilation_complete ::X::compilation_message & \ + ] + } + + ## Start ASL (Assembler) + # @parm String work_dir - Current working directory + # @parm String input_file - Assembler source file to compile + # @parm String project_directory - Project directory (for debug file) + # @return Int - Compiler PID + proc asl_compile {work_dir input_file project_directory} { + variable project_dir ;# String: Project directory + variable compiler_used ;# Int: Compiler ID (1 == ASEM-51; 2 == ASL; 3 == AS31, other values have no meaning) + variable input_filename ;# String: Name of file to compile (without extension) + variable assembler_ASL_addcfg ;# Current ASL additional configuration + global argv + + set compiler_used 2 + set project_dir $project_directory + set input_filename [file join $work_dir [file rootname $input_file]] + backup_and_remove {hex lst map adf} + set asl_opts [determinate_asl_options] + set additional_commands {} + if {$assembler_ASL_addcfg(ihex)} { + append additional_commands {&& p2hex "} $input_filename.p {" "} $input_filename.hex {"} + } + + ::X::messages_text_append [::Compiler::msgc {S}][mc "\n\nStarting compiler ..."] + ::X::messages_text_append "\ncd \"$work_dir\"\nasl $asl_opts \"$input_file\"" + if {[catch { + cd $work_dir + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nError: Unable to change working directory to '%s'" $work_dir] + } + return [exec -- /bin/sh -c "asl $asl_opts \"$input_file\" $additional_commands" |& \ + tclsh "${::LIB_DIRNAME}/external_command.tcl" "[tk appname]" \ + ::ExternalCompiler::ext_compilation_complete ::X::compilation_message & \ + ] + } + + ## Create file containg MD5 hashes of source files + # Suitable for C language only! + # This file will be later used to chech wheter any of these files was changed or not. + # @return void + proc create_hashes_file {} { + variable input_filename ;# String: Name of file to compile (without extension) + + # List of files included files in the main file + set included_files [list] + + # Open C DeBug file generated by SDCC compiler + if {[catch { + set cdb_file [open $input_filename.cdb r] + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nUnable to find \"%s\"" [file rootname $input_filename].cdb] + return + } + + # Open the hashes file for writing (possibly create the file) + if {[catch { + set hs_file [open $input_filename.hashes w 420] + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nUnable to create \"%s\"" [file rootname $input_filename].hashes] + catch {close $cbd_file} + return + } + + # Iterate over lines in the C DeBug file and list all included source files + while {![eof $cdb_file]} { + set line [gets $cdb_file] + + if {[string first {L:C$} $line]} { + continue + } + + set line [string replace $line 0 3] + set line [string replace $line [string first {$} $line] end] + + if {[lsearch -ascii -exact $included_files $line] == -1} { + lappend included_files $line + } + } + + # Compute MD5 hash for each of the included files + foreach filename $included_files { + catch { + puts $hs_file "[::md5::md5 -hex -file $filename] \"$filename\" " + } + } + + # Clean up + catch {close $cdb_file} + catch {close $hs_file} + } + + ## This function must be called after exteral compiler finished its work + # @parm Int action = 0 - Action to perform after successfull compilation + # 0 - No action + # 1 - Copy <file>.ihx to <file>.hex + # @return void + proc ext_compilation_complete args { + variable input_filename ;# String: Name of file to compile (without extension) + variable compiler_used ;# Int: Compiler ID (1 == ASEM-51; 2 == ASL; 3 == AS31, other values have no meaning) + variable assembler_ASEM51_addcfg;# Current ASEM-51 assembler configuration + variable assembler_ASL_addcfg ;# Current ASL additional configuration + variable assembler_AS31_addcfg ;# Current AS31 additional configuration + + # Compilation successfull + if {$::X::compilation_successfull} { + + # Create MCU 8051 IDE assembler debug file - + switch -- $compiler_used { + 1 { ;# - from ASEM-51 code listing (*.lst) + if {$assembler_ASEM51_addcfg(adf) && ![asem_51_analyze]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nUnable to find \"%s\"\n\tMCU 8051 IDE debug file (*.adf) could not be generated\n\tPLEASE CHECK YOUR %s CONFIGURATION" [file rootname $input_filename].lst {ASEM-51}] + } + } + 2 { ;# - from ASL native debug file (*.map) + if {$assembler_ASL_addcfg(adf) && ![asl_analyze]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nUnable to find \"%s\"\n\tMCU 8051 IDE debug file (*.adf) could not be generated\n\tPLEASE CHECK YOUR %s CONFIGURATION" [file rootname $input_filename].map {ASL}] + } + } + 3 { ;# - from AS31 code listing file (*.lst) + if {$assembler_ASL_addcfg(adf) && ![as31_analyze]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nUnable to find \"%s\"\n\tMCU 8051 IDE debug file (*.adf) could not be generated\n\tPLEASE CHECK YOUR %s CONFIGURATION" [file rootname $input_filename].lst {AS31}] + } + } + 0 { ;# SDCC used: Create .hashes file from .cdb file + create_hashes_file + } + } + ::X::messages_text_append [::Compiler::msgc {S}][mc "\nCompilation successful"] + + # Perform specified after successfull compilation + switch -- [lindex $args 0] { + 0 { ;# No action + } + 1 { ;# Copy <file>.ihx to <file>.hex + catch { + file rename -force -- "$input_filename.hex" "$input_filename.hex~" + } + catch { + file copy -force -- "$input_filename.ihx" "$input_filename.hex" + } + } + } + + # Compilation failed + } { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nCompilation FAILED"] + } + ::X::ext_compilation_complete + } + + ## Create MCU 8051 IDE assembler debug file from AS31 code listing + # @return Bool - 1 == success; 0 == failure + proc as31_analyze {} { + variable project_dir ;# String: Project directory + variable working_dir ;# String: Compiler working directory + variable input_filename ;# String: Name of file to compile (without extension) + variable input_file_base;# String: Full file name of the primary source file + + # Local variables + set line_number 0 ;# Line number in LST file + set adf_line 0 ;# Line number to record in ADF file + set adf_code {} ;# Processor code in decimal representation (for ADF file) + set address {} ;# Address in code memory + set processor_code {} ;# Processor code read from LST file + + # Try to open code listing file and some tempotary debug file + if {[catch { + set lst_file [open $input_filename.lst r] + set adf_file [open $input_filename.adf w 420] + } result]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "File access error:\n%s" $result] + return 0 + } + + # Write file header + puts $adf_file "# Assembler Debug File created by ${::APPNAME}" + puts $adf_file "# Used assembler: AS31" + puts $adf_file "# Date: [clock format [clock seconds] -format {%D}]" + + # Write MD5 of the source file + puts -nonewline $adf_file [::md5::md5 -hex -file $input_file_base] + puts -nonewline $adf_file { } + puts -nonewline $adf_file [string replace $input_file_base 0 [string length $project_dir]] + + # One pass compilation LST -> ADF + while {![eof $lst_file]} { + incr line_number + + # Read one line from the code listing + set line [string range [gets $lst_file] 0 17] + + # Lines which does not contain address or code will be ignored + #+ but line number counter must be still incremented on these lines + if {[regexp {^\s*$} $line]} { + continue + } + + set address [string trim [string range $line 0 3]] + set code [string trim [string range $line 6 17]] + + # If there is no processor code then skip the line + if {$code == {}} { + continue + } + + # Convert processor code to format suitable for this application, + #+ that means convert list of HH to list of DDD + if {[catch { + set adf_code {} + foreach h $code { + scan $h %x h + lappend adf_code $h + } + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "Unable to understand formulation at line %s in file %s" $line_number $input_filename.lst] + close $lst_file + close $adf_file + return 0 + } + + # If there is no address then append the current code to the last ADF record + if {$address == {}} { + puts -nonewline $adf_file { } + puts -nonewline $adf_file $adf_code + } { + if {[catch { + scan $address %x address + }]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "Unable to understand formulation at line %s in file %s" $line_number $input_filename.lst] + close $lst_file + close $adf_file + return 0 + } + set adf_line $line_number + + puts -nonewline $adf_file "\n0 $adf_line $address $adf_code" + } + } + + # Close all files and finalize ... + puts $adf_file {} + close $lst_file + close $adf_file + return 1 + } + + ## Create MCU 8051 IDE assembler debug file from ASEM-51 code listing + # @return Bool - 1 == success; 0 == failure + proc asem_51_analyze {} { + variable project_dir ;# String: Project directory + variable working_dir ;# String: Compiler working directory + variable input_filename ;# String: Name of file to compile (without extension) + variable input_file_base;# String: Full file name of the primary source file + + # Local variables + array set line_number {} ;# Array of Int: Line number within certain inslusion level + set inclusion_level 0 ;# Int: Current inclusion level + set file_number 0 ;# Int: Current file number (number of included file in $included_files) + set file_number_changed 0 ;# Bool: Next line includes new file + set address 0 ;# Int(H|D): Address in machine code + set code {} ;# List of Int(H|D): Machine code + set included_files [list $input_file_base] ;# List of all included files (unique, unsorted) + + # Try to open code listing file and some tempotary debug file + if {[catch { + set lst_file [open $input_filename.lst r] + set adf_file [open $input_filename._adf w 420] + } result]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "File access error:\n%s" $result] + return 0 + } + + # Initialize line number counter + for {set i 0} {$i < 100} {incr i} { + set line_number($i) 0 + } + + # One pass compilation LST -> ADF + while {![eof $lst_file]} { + # Read 1 line + set line [gets $lst_file] + + # Normal line coresponding to certain line in source code + if {[regexp {^ *\d+:(..)?} $line inclusion_level]} { + # Extract numbers after "line_num: inc_lvl " + set line [string range $line [string length $inclusion_level] end] + regexp {^[0-9a-fA-F\s]+} $line code + + # Determinate address and machine code + set address [lindex $code 0] + set code [lrange $code 1 end] + + # Determinate inclusion level + set inclusion_level [string trim [string range $inclusion_level end-1 end]] + if {$inclusion_level == {} || [string index $inclusion_level end] == {:}} { + set inclusion_level 0 + set file_number 0 + } + incr line_number($inclusion_level) + + # Continuation of previous unfinished line + } elseif {![string first { } $line]} { + set address [lindex $line 0] + set code [lrange $line 1 end] + + # Other lines + } else { + continue + } + + # Detect directive "$INCLUDE(file)" + if {[regexp -nocase -- {.*\$include\s*\([^\(\)]+\)} $line line]} { + regsub {;.+$} $line {} line + set line [string trim $line] + regexp -nocase -- {\$include\s*\([^\(\)]+\)} $line line + if {$line == {}} { + continue + } + set line [string replace $line 0 7] + set line [string trim $line {( )}] + set line [file normalize [file join $working_dir $line]] + set file_number_changed 1 + set file_number [lsearch -ascii -exact $included_files $line] + if {$file_number == -1} { + set file_number [llength $included_files] + lappend included_files $line + } + continue + } + + # Next file included -> Reset lines counter + if {$file_number_changed} { + set line_number($inclusion_level) 1 + set file_number_changed 0 + } + + # Convert machine code from hexadecimal to decimal value + set code_dec {} + foreach byte $code { + if {[string length $byte] != 2 || ![string is xdigit -strict $byte]} { + break + } + scan $byte %x byte + lappend code_dec $byte + } + + # Machine code must not be empty + if {$code_dec == {}} {continue} + + # Check for valid address + if {[string length $address] != 4 || ![string is xdigit -strict $address]} { + continue + } + + # Write line to tempotary debug file + scan $address %x address + puts -nonewline $adf_file [list $file_number $line_number($inclusion_level) $address] + puts -nonewline $adf_file { } + puts $adf_file $code_dec + } + close $adf_file + + # Open final debug file + if {[catch { + set adf_file [open $input_filename.adf w 420] + } result]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "File access error:\n%s" $result] + return 0 + } + # Write file header + puts $adf_file "# Assembler Debug File created by ${::APPNAME}" + puts $adf_file "# Used assembler: ASEM-51" + puts $adf_file "# Date: [clock format [clock seconds] -format {%D}]" + # Create list of included files with MD5 hashes + set hashes_and_files {} + set project_dir_len [string length $project_dir] + foreach filename $included_files { + if {[catch { + lappend hashes_and_files [::md5::md5 -hex -file $filename] + } result]} { + lappend hashes_and_files 0 + ::X::messages_text_append [::Compiler::msgc {E}][mc "File access error:\n%s" $result] + } + if {![string first $project_dir $filename]} { + set filename [string replace $filename 0 $project_dir_len] + } + lappend hashes_and_files $filename + } + # Write list of included files + puts $adf_file $hashes_and_files + # Copy content of tempotary debug file to final debug file + if {[catch { + set adf__file [open $input_filename._adf r] + } result]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "File access error:\n%s" $result] + return 0 + } + while {![eof $adf__file]} { + puts $adf_file [gets $adf__file] + } + + # Close all files and delete tempotary file + close $adf__file + close $lst_file + close $adf_file + file delete -force $input_filename._adf + return 1 + } + + ## Create MCU 8051 IDE assembler debug file from ASL code listing + # @return Bool - 1 == success; 0 == fail + proc asl_analyze {} { + variable project_dir ;# String: Project directory + variable input_filename ;# String: Name of file to compile (without extension) + + # Try to open all required files + if {[catch { + set map_file [open $input_filename.map r] ;# ASL debug file + set hex_file [open $input_filename.hex r] ;# Machine code + set adf_file [open $input_filename.adf w 420] ;# MCU 8051 IDE debug file + } result]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "File access error:\n%s" $result] + return 0 + } + + # Load machine code + ::IHexTools::free_resources + if {![::IHexTools::load_hex_data [read $hex_file]]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "Compilation error:\nFile \"%s\" is not a valid Intel® HEX 8 file" $input_filename.hex] + return 0 + } + close $hex_file + + # Initialize local variables + set filenames {} + set hashes_and_files {} + set filename {} + set project_dir_len [string length $project_dir] + set read_values 0 + + ## 1st pass + # Determinate list of included files (and list of files and MD5 hashes) + while {![eof $map_file]} { + # Get significant line from the code + set line [gets $map_file] + if {$line == {Segment CODE}} { + set read_values 1 + continue + } + if {$line == {}} { + break + } + if {!$read_values} { + continue + } + + # Ignore lines which doesn't start with "File " + if {[string first {File } $line]} { + continue + } + + # Determinate raw name of included file + set filename [string replace $line 0 4] + + # Adjust list of included files + if {[lsearch $filenames $filename] != -1} { + lappend filenames $filename + continue + } + + # Determinate final file name and its MD5 hash + lappend filenames $filename + set filename [file join $project_dir [file normalize $filename]] + if {[catch { + lappend hashes_and_files [::md5::md5 -hex -file $filename] + } result]} { + ::X::messages_text_append [::Compiler::msgc {E}][mc "\nFile access error:\n%s" $result] + lappend hashes_and_files {0} + } + if {![string first $project_dir $filename]} { + set filename [string replace $filename 0 $project_dir_len] + } + + # Adjust list of files and MD5 hashes + lappend hashes_and_files $filename + continue + } + + # Create ADF file header + seek $map_file 0 + puts $adf_file "# Assembler Debug File created by ${::APPNAME}" + puts $adf_file "# Used assembler: ASL" + puts $adf_file "# Date: [clock format [clock seconds] -format {%D}]" + puts $adf_file $hashes_and_files + unset hashes_and_files + + ## 2nd (final) pass + # Create ADF (Assembler Debug File) + set last_line -1 ;# + set last_address -1 ;# + set last_file_num 0 ;# + set line_number 0 ;# + set file_number 0 ;# + set read_values 0 ;# + while {![eof $map_file]} { + # Get significant line from the code + set line [gets $map_file] + if {$line == {Segment CODE}} { + set read_values 1 + continue + } + if {$line == {}} { + break + } + if {!$read_values} { + continue + } + + # Change file number + if {![string first {File } $line]} { + set file_number [lsearch $filenames [string replace $line 0 4]] + continue + } + + # Create ADF record(s) + foreach item $line { + # Determinate line number and address + set item [split $item {:}] + set line_number [lindex $item 0] + scan [lindex $item 1] %x address + + # Handle firts record (first of all) + if {$last_line == -1} { + set last_line $line_number + set last_address $address + set last_file_num $file_number + continue + } + + # Write record + write_to_adf_from_hex $adf_file $last_address \ + [expr {$address - 1}] $last_line $last_file_num + set last_line $line_number + set last_address $address + set last_file_num $file_number + } + } + + # Write last record (last of all) + if {$last_line != -1} { + write_to_adf_from_hex $adf_file $last_address \ + ${::IHexTools::highest_addr} \ + $last_line $last_file_num + } + + # Clean up + ::IHexTools::free_resources + close $map_file + close $adf_file + return 1 + } + + ## Write record to MCU 8051 IDE assembler debug file (*.adf) + # Auxiliary procedure for procedure asl_analyze + # @parm Chanel adf_file - Target file + # @parm Int start_address - Starting address in machine code + # @parm Int end_address - End address in machine code + # @parm Int linenum - Line number + # @parm Int filenum - File number (ID of included file) + # @return void + proc write_to_adf_from_hex {adf_file start_address end_address linenum filenum} { + # Get machine code from NS ::IHexTools + set code {} + for {set i $start_address} {$i <= $end_address} {incr i} { + set val [::IHexTools::get_value $i] + if {$val > -1} { + lappend code [expr "0x$val"] + } { + lappend code 0 + } + } + + # Write to file + if {$linenum == {}} { + set linenum 0 + } + puts -nonewline $adf_file [list $filenum $linenum $start_address] + puts -nonewline $adf_file { } + puts $adf_file $code + } + + ## Determinate CLI options for external compiler sdcc + # @return String - Options for sdcc + proc determinate_sdcc_options {} { + variable sdcc_bool_options ;# Default SDCC boolean options + variable sdcc_string_options ;# Default SDCC string options + variable sdcc_optional_string_options ;# Default SDCC optional string options + variable sdcc_scs_string_options ;# Default semicolon separated optional string options + + set result {} + + # Boolean options + foreach key [array names sdcc_bool_options] { + if {$sdcc_bool_options($key)} { + append result { } $key + } + } + + # String options + foreach key [array names sdcc_string_options] { + append result { } [regsub -all {\n} $sdcc_string_options($key) {}] + } + + # Optional string options + foreach key [array names sdcc_optional_string_options] { + set value $sdcc_optional_string_options($key) + if {$value != {}} { + if {[regexp {\s} $value]} { + append result { } $key { } "\"" $value "\"" + } { + append result { } $key { } $value + } + } + } + + # Semicolon separated optional string options + foreach key [array names sdcc_scs_string_options] { + set values $sdcc_scs_string_options($key) + foreach value [split $values {;}] { + if {$value != {}} { + if {[regexp {\s} $value]} { + append result { } $key { } "\"" $value "\"" + } { + append result { } $key { } $value + } + } + } + } + + return $result + } + + ## Determinate CLI options for external assembler ASEM-51 + # @return String - Options for ASEM-51 + proc determinate_asem51_options {} { + variable assembler_ASEM51_config ;# Current ASEM-51 assembler configuration + + set result $assembler_ASEM51_config(custom) + if {$assembler_ASEM51_config(-i) != {}} { + append result { -i } {"} $assembler_ASEM51_config(i) {"} + } + foreach opt {--omf-51 --columns --verbose} { + if {$assembler_ASEM51_config($opt)} { + append result { } $opt + } + } + + return $result + } + + ## Determinate CLI options for external assembler AS31 + # @return String - Options for AS31 + proc determinate_as31_options {} { + variable assembler_AS31_config ;# Current AS31 assembler configuration + + set result {} + if {$assembler_AS31_config(custom) != {}} { + append result { } ${assembler_AS31_config(custom)} + } + foreach opt {-l} { + if {$assembler_AS31_config($opt)} { + append result { } $opt + } + } + foreach opt {-F -A} { + if {$assembler_AS31_config($opt) != {}} { + append result { } $opt $assembler_AS31_config($opt) + } + } + + return $result + } + + ## Determinate CLI options for external assembler ASL + # @return String - Options for ASL + proc determinate_asl_options {} { + variable assembler_ASL_config ;# Current ASL assembler configuration + + set result {-gnuerrors} + if {$assembler_ASL_config(custom) != {}} { + append result { } ${assembler_ASL_config(custom)} + } + foreach opt {-A -a -C -c -h -I -L -M -P -n -quiet -s -u -U -w -x} { + if {$assembler_ASL_config($opt)} { + append result { } $opt + } + } + foreach opt {-r -i -g -cpu} { + if {$assembler_ASL_config($opt) != {}} { + append result { } $opt { "} $assembler_ASL_config($opt) {"} + } + } + + return $result + } + + ## Initialize NS variables + # @return void + proc initialize {} { + # Assembler + array set ::ExternalCompiler::assembler_ASEM51_addcfg \ + $::ExternalCompiler::assembler_ASEM51_addcfg_def + array set ::ExternalCompiler::assembler_ASEM51_config \ + $::ExternalCompiler::assembler_ASEM51_config_def + array set ::ExternalCompiler::assembler_ASL_addcfg \ + $::ExternalCompiler::assembler_ASL_addcfg_def + array set ::ExternalCompiler::assembler_ASL_config \ + $::ExternalCompiler::assembler_ASL_config_def + array set ::ExternalCompiler::assembler_AS31_addcfg \ + $::ExternalCompiler::assembler_AS31_addcfg_def + array set ::ExternalCompiler::assembler_AS31_config \ + $::ExternalCompiler::assembler_AS31_config_def + + # SDCC + array set ::ExternalCompiler::sdcc_bool_options \ + $::ExternalCompiler::sdcc_bool_options_def + array set ::ExternalCompiler::sdcc_string_options \ + $::ExternalCompiler::sdcc_string_options_def + array set ::ExternalCompiler::sdcc_optional_string_options \ + $::ExternalCompiler::sdcc_optional_string_options_def + array set ::ExternalCompiler::sdcc_scs_string_options \ + $::ExternalCompiler::sdcc_scs_string_options_def + } +} + +# Initialize NS variables +ExternalCompiler::initialize |