#!/bin/sh # the next line restarts using tclsh \ exec tclsh "$0" "$@" # hcdl - HC11 Bootstrap Downloader Revision 0.2 # for A1, A8 (and probably E2) family members. # written entirely in TCL. # # Copyright 1999, 2000, Loren Wyard-Scott (wyard@ee.ualberta.ca) # The author hereby grants permission to use this code in # whole or in part as long as credit is given. # # Usage: # ------ # hcdl file [-baud baudrate] [-port serialport] [-mode dispmode] # # where # file is the name of the .s19 file (including the # extension) to download starting at address 0x0000. # baudrate is an integer value specifying the bootstrap # download baud rate. This is a XTAL-dependent # value and should be set to the crystal frequency # divided by 1024 for the maximum transfer rate. # This is an optional argument that defaults to # the value specified below. # serialport is the name of the serial port that will be # used. eg) /dev/ttyS1 # This is an optional argument that defaults to # the value specified below. # dispmode is one of the following: # colour - uses ANSI escape sequences for a pretty # display. # quiet - displays nothing except error messages. # Useful, perhaps, when this script is # called by another program. # line - displays information, but not using # a colorful display. # Again, this is an optional argument that defaults to # the value specified below. # # # Requires TCL version 8.1 or greater # available from ftp.scriptics.com # (version 8.0 has faulty serial-port routines). # # Installation instructions: # -------------------------- # 1. Copy this file to /usr/local/bin or wherever you want # it to reside. # 2. Set permissions to allow execution by whomever you # feel comfortable giving access to serial ports. # 3. Whoever uses this program will also need to access # the serial port in both read and write capacities. # Set permissions accordingly. # 4. Adjust the path to tclsh by modifying the first line # of this file. # 5. Edit the default variable values to match your # system (HC11 board and PC). # # # Lessons learned from writing this program: # ------------------------------------------ # 1. TCL version 8.0 does not correctly support serial # port mode control using fconfigure. This appears # to be fixed in version 8.1.1 # 2. When sending binary data, use the binary command # to construct the string (or character). This # seems to prevent confusion for the TCL interpreter # when dealing with binary codes for special characters. # 3. When receiving binary data, use the binary command # to convert the data to some other representation (perhaps # hex), again to avoid special character substitution. # 4. To transfer binary data through the serial port and to # avoid bizarre translation, use the -translation binary # option in fconfigure. # # Future Directions: # ------------------ # 1. Add support for other HC11 family members: notably the E9. # # Revision History # ---------------- # Revision 0.2 -- Wed May 3 10:38:32 MDT 2000 # - Changed serial port to non-blocking operating mode. # - Added polling loop which aborts after so many unsuccessfull reads of # the serial port. Made somewhat CPU-independent by using the "after" # command. # - Added lockfile serial port access semaphore. In order to avoid # coercing execution permissions to allow file creation in /var/lock, # the lock file is placed in the user's home directory: ~/LCK..ttySx # (the same naming format that minicom uses). # ###################################################################### # The following variables may need to be changed depending upon # your system configuration. # If no serial port is specified on the command-line, this # one is used. set serialport /dev/ttyS1 # If no baudrate is specified on the command-line, this one is used. set baudrate 4800; # 4800 <-> 4.9152MHz XTAL # 7800 <-> 8.0000MHz XTAL # If no display mode is specified, this one is used. set dispmode colour ###################################################################### set Revision 0.2 # Establish the maximum time that the non-blocking serial port # should be polled prior to considering the line dead (i.e. no response). # Variable MAXPOLLCOUNT is the number of times that the line should be # read (and nothing received), delay for 1 ms, and then re-read. set MAXPOLLCOUNT 100; if {$tcl_version < 8.1} { puts stdout "This is TCL version $tcl_version. You need at least 8.1." puts stderr "This is TCL version $tcl_version. You need at least 8.1." exit 1 } # Parse the command-line arguments. if {$argc < 1} { puts stdout "HC11 Bootstrap Downloader" puts stdout "Usage:" puts stdout "hcdl file \[-baud baudrate\] \[-port portspec\] \[-mode dispmode\]" puts stdout "where" puts stdout "file is the name of the .s19 file (including the" puts stdout " extension) to download starting at address 0x0000." puts stdout "baudrate is an integer value specifying the bootstrap" puts stdout " download baud rate. This is a XTAL-dependent" puts stdout " value and should be set to the crystal frequency" puts stdout " divided by 1024 for the maximum transfer rate." puts stdout " This is an optional argument that defaults to" puts stdout " the value $baudrate." puts stdout "serialport is the name of the serial port that will be" puts stdout " used. This is an optional argument that" puts stdout " defaults to $serialport." puts stdout "dispmode is one of the following:" puts stdout " colour - uses ANSI escape sequences for a pretty" puts stdout " display." puts stdout " quiet - displays nothing except error messages." puts stdout " Useful, perhaps, when this script is" puts stdout " called by another program." puts stdout " line - displays information, but not using" puts stdout " a colorful display." puts stderr "hcdl called with incorrect syntax." exit 1 } # The name of the file should be the first argument on the command # line. set filename [lindex $argv 0] if [catch "open $filename RDONLY" fileID] { puts stdout $fileID puts stderr $fileID exit 1 } # Now parse the optional arguments in pairs. set listlength [llength $argv] for {set argument 1} {[expr $argument+1] < $listlength} {incr argument 2} { set option [lindex $argv $argument] set value [lindex $argv [expr $argument+1]] if {[string match $option -baud]} { set baudrate $value } elseif {[string match $option -port]} { set serialport $value } elseif {[string match $option -mode]} { set dispmode $value } else { puts stdout "Unknown option/value: $option $value" puts stderr "hcdl called with incorrect options." exit 1 } } # Done parsing the command-line arguments. # Reserve the serial port using a lockout file "LCK..ttySx" in the user's home directory. # Create the lock file name. set lockname "$env(HOME)/LCK..[lindex [split $serialport /] end]" if [catch "exec lockfile -r 1 $lockname" result] { puts stderr "Could not create lock file $lockname." puts stderr " $result" puts stderr "Is there another program currently using $serialport?" puts stderr "If not, please type: rm -f $lockname" exit 1 } # Open the serial port. if [catch "open $serialport RDWR" serID] { puts stdout "Problems opening serial port: $serialport" puts stderr "hcdl could not open serial port." ExitProgram 1 } # Set the baudrate and the asynchronous format. if [catch "fconfigure $serID -mode $baudrate,n,8,1 -translation binary -blocking 0"] { puts stdout "Problem attempting to set serial transfer rate of $baudrate." puts stderr "hcdl could not establish serial transfer rate." ExitProgram 1 } if [string match $dispmode colour] { puts stdout "\x1b\[?25l\x1b\[2J\x1b\[2;25H\x1b\[41m-----Bootstrap Downloader------" puts stdout "\x1b\[3;25H HC11 A1/A8/E2 Revision $Revision " puts stdout "\x1b\[4;25H Copyright 2000 L. Wyard-Scott " puts stdout "\x1b\[5;25H-------------------------------\x1b\[m" puts stdout "\x1b\[6;25HFilename: $filename" puts stdout "\x1b\[17;25H\x1b\[1mStatus: \x1b\[m" puts stdout "\x1b\[19;25H\x1b\[1mByte of 255:\x1b\[m" puts stdout "\x1b\[20;25H\x1b\[1mReturned byte: \x1b\[m" } elseif [string match $dispmode line] { puts stdout "-----Bootstrap Downloader------" puts stdout " HC11 A1/A8/E2 Revision $Revision " puts stdout " Copyright 2000 L. Wyard-Scott " puts stdout "-------------------------------" puts stdout "Filename: $filename" } # Show the connection speed. if [string match $dispmode colour] { puts stdout "\x1b\[7;25H\x1b\[mSerial Mode: [fconfigure $serID -mode]" } elseif [string match $dispmode line] { puts stdout "Serial Mode: [fconfigure $serID -mode]" } # Create a global array that will contain the # data read in from the file. set sdata(0) "" ###################################################################### proc readfile {fileID} { # Read in an .s19 file into global array called sdata. global sdata # Grab the S record type. set rectype [read $fileID 2] if ![string match S9 $rectype] { # puts stdout "Processing record: $rectype" set numdata [expr 0x[read $fileID 2]] set numdata [expr $numdata -3] # puts stdout "Number of pieces of data: $numdata" set addr [expr 0x[read $fileID 4]] # puts stdout "Starting address: $addr" for {set i 0} {$i<$numdata} {incr i} { # puts stdout "$i-" nonewline set sdata([expr $addr + $i]) [read $fileID 2] } gets $fileID readfile $fileID } } ###################################################################### proc filldata {length} { # Ensures that all array elements from 0 to length of global # array sdata are initialized to FF. global sdata for {set i 0} {$i<$length} {incr i} { set sdata($i) FF } } ###################################################################### proc printdata {length} { global sdata set numblock [expr $length/32] for {set block 0} {$block < $numblock} {incr block} { puts stdout "$block: " nonewline for {set i 0} {$i < 32} {incr i} { puts stdout $sdata([expr $i + 32 * $block]) nonewline } puts stdout ""; # Append new line. } } ###################################################################### proc senddata {length serID} { # Actual routine to send info to HC11 bootstrap program. # sends length bytes through serial port identified by serID. # Data is contained in global sdata. global sdata global dispmode global MAXPOLLCOUNT # First send FF to synchronize with the HC11. if [string match $dispmode colour] { puts stdout "\x1b\[17;34H Sending Synchronization Byte" } elseif [string match $dispmode line] { puts stdout "Sending Synchronization Byte" } sendbyte FF $serID # Nothing is transmitted by the HC11. This is used to set # the baudrate. if [string match $dispmode colour] { puts stdout "\x1b\[17;34H Transmitting File Data " } elseif [string match $dispmode line] { puts stdout "Transmitting File Data " } for {set i 0} {$i<$length} {incr i} { set byte $sdata($i) if [string match $dispmode colour] { puts stdout "\x1b\[19;30H$i" puts stdout "\x1b\[19;43H$byte" } elseif [string match $dispmode line] { puts stdout "." nonewline flush stdout } # Actually send the byte out. sendbyte $byte $serID # Set a timer to abort if nothing is received (rather than block). # Get the handshaking byte. set handshake [receivebyte $serID $MAXPOLLCOUNT] if [string match NoChar $handshake] { if [string match $dispmode colour] { puts stdout "\x1b\[17;34H Handshaking failure. " puts stdout "\x1b\[24;1H\x1b\[m" } puts stderr "Error: Handshaking byte not received." puts stderr " Is the HC11 in bootstrap download mode?" ExitProgram 1 } # Both the byte and handshake variables are capital hex # and can now be compared. if ![string match $byte $handshake] { if [string match $dispmode colour] { puts stdout "\x1b\[17;34H Handshaking error! " puts stdout "\x1b\[18;34H Sent: $byte Received: $handshake" puts stdout "\x1b\[24;1H\x1b\[m" } else { puts stdout "Handshaking error! " puts stdout "Sent: $byte Received: $handshake" } puts stdout "hcdl encountered handshaking error." ExitProgram 1 } if [string match $dispmode colour] { puts stdout "\x1b\[20;43H$handshake" } } if [string match $dispmode colour] { puts stdout "\x1b\[17;34H Successfully Completed. " } elseif [string match $dispmode line] { puts stdout " Successfully Completed. " } } ###################################################################### proc sendbyte {hexvalue serID} { # Sends a byte specfied in hex out to the serial port. set data [binary format H2 $hexvalue] # The NULL needs to be handled just a little differently. if [string match 00 $hexvalue] { puts $serID \x00 nonewline } else { puts $serID $data nonewline } flush $serID # puts stdout "Sent: $hexvalue " nonewline } ###################################################################### proc receivebyte {serID PollCount} { # Receives a byte from the specified serial port and returns # it in 2-digit hex form. The incoming information is polled. # If the serial port is read PollCount times, then the # routine returns the string "NoChar" rather than the byte received. set PollNumber 0 while {$PollNumber < $PollCount} { binary scan [read $serID 1] H2 value # Make the Hex letters capital for comparison. if [info exists value] { set value [string toupper $value] return $value } else { after 1; # Delay 1 millisecond. incr PollNumber } } return "NoChar" } ###################################################################### proc comparebyte {arg1 arg2 errormsg} { # Procedure to compare the 2-digit (hex) arguments. If # they are not equal, the error message is displayed and # the application aborted. global dispmode if ![string match $arg1 $arg2] { if [string match $dispmode colour] { puts stdout "\x1b\[17;34H $errormsg" puts stdout "\x1b\[18;34H Comparison: $arg1, $arg2" puts stdout "\x1b\[24;1H\x1b\[m" } else { puts stdout $errormsg puts stdout "Comparison: $arg1, $arg2" } ExitProgram 1 } } ###################################################################### proc ExitProgram {exitvalue} { # Procedure to intercept termination of the program. Removes the # lock file and closes the files. global serID global fileID global lockname # Close the files. close $serID close $fileID # Remove the lockfile. exec rm -f $lockname exit $exitvalue } ###################################################################### # Fill the entire array with value 'FF'. filldata 256 # Read the information from the .s19 file. readfile $fileID # Display the contents of the array if in an appropriate mode. if { [string match $dispmode colour] | [string match $dispmode line] } { # Position the cursor if in colour mode. if [string match $dispmode colour] { puts stdout "\x1b\[8;1H" } # Display the contents. printdata 256 } senddata 256 $serID if [string match $dispmode colour] { puts stdout "\x1b\[24;1H\x1b\[m" } ExitProgram 0