Properly install packihx utility
[fw/sdcc] / src / packihx
1 #!/usr/local/bin/perl -w
2
3 # Quick & dirty perl hack to pack an Intel Hex format output file.
4 # Will consolidate lines up to $outChunk bytes per line.
5
6 use integer;
7
8 # 16 bytes per line is default.
9 $outChunk = 16;
10
11 $nextOffset = 0;
12 $pendingData = "";
13 $pendingDataStart = 0;
14 $pendingDataCount = 0;
15 $lastExtendedOffset = 0;
16
17 while (<>)
18 {
19     if (/:([\dABCDEFabcdef]{2})([\dABCDEFabcdef]{4})(\d\d)(.*)([\dABCDEFabcdef]{2})/)
20     {
21         # $1 = record len; $2 = offset; $3 = record type; $4 = data; $5 = checksum.
22         $recordLen = hex $1;
23         $recordOffset = hex $2;
24
25         # Sanity checks...
26         if ((length $4) % 2 == 1)
27         {
28             print STDERR "packihx: odd data length $4 in line $.\n";
29             exit -1;
30         }
31         if ((length $4) / 2 != $recordLen)
32         {
33             print STDERR "packihx: data len mismatch in line $.: $1 != len $4\n";
34             exit -1;
35         }
36         if (checksum("$1$2$3$4") != hex $5)
37         {
38             print STDERR "packihx: bad checksum in line $.: ";
39             printf STDERR "wanted %02X, got %s\n", checksum("$1$2$3$4"), $5;
40             exit -1;
41         }
42
43         if ($3 != 0)
44         {
45             # If this is an extended data offset record, and it
46             # matches the expected extended data offset, then
47             # we can simply discard it.
48             if (($3 == 4) && ($recordLen == 2))
49             {
50                 my $extendedOffset = hex $4;
51                 if ($extendedOffset == $lastExtendedOffset)
52                 {
53                     next;
54                 }
55                 $lastExtendedOffset = $extendedOffset;
56             }
57             # Not a data record. Flush anything pending, then write this
58             # line as is.
59             &flush_pending_data;
60             $pendingDataStart = 0;
61             $nextOffset = 0;
62             print;
63         }
64         else
65         {
66             # If this isn't contigous with the last record, 
67             # flush anything pending and start collecting anew.
68             if ($recordOffset != $nextOffset)
69             {
70                 &flush_pending_data;
71                 $pendingDataStart = $recordOffset;
72                 $nextOffset = $pendingDataStart;
73             }
74
75             # Append this data onto the pending data.
76             $pendingData .= $4;
77             $pendingDataCount += $recordLen;
78             $nextOffset += $recordLen;
79
80             # If we'va accumulated at least an outChunk
81             # worth of data, write it out.
82             while ($pendingDataCount >= $outChunk)
83             {
84                 &write_pending_line($outChunk);
85             }
86         }           
87     }
88     else
89     {
90         print STDERR "packihx: don't recognize line $.: $_";
91         exit -1;
92     }
93 }
94 &flush_pending_data;
95
96 exit 0;
97
98 # Write all buffered data.
99 sub flush_pending_data
100 {
101     while ($pendingDataCount > $outChunk)
102     {
103         &write_pending_line($outChunk);
104     }
105     if ($pendingDataCount)
106     {
107         &write_pending_line($pendingDataCount);
108     }
109 }
110
111 # Write a given number of bytes of buffered output.
112 sub write_pending_line
113 {   
114     my $lineLen = shift(@_);
115
116     if ($lineLen > $pendingDataCount)
117     {
118         print STDERR "packihx: internal error: asked to write $lineLen bytes ";
119         print STDERR "with only $pendingDataCount bytes buffered.\n";
120         exit -1;
121     }
122
123     my $data = substr $pendingData, 0, $lineLen * 2;
124     my $toWrite = sprintf "%02X%04X00%s", $lineLen, $pendingDataStart, $data;
125
126     my $cks = checksum($toWrite);
127
128     printf ":%s%02X\n", $toWrite, $cks;
129
130     $pendingDataStart += $lineLen;
131     $pendingDataCount -= $lineLen;  
132     $pendingData = substr $pendingData, $lineLen * 2;
133 }
134
135 # Generate a IHX checksum.
136 sub checksum
137 {
138    my $line = shift(@_);
139    my $offset = 0;
140    my $cks = 0;
141    
142    while ($offset < length $line)
143    {
144        my $byte = substr $line, $offset, 2;
145        $cks += hex $byte;
146        $offset += 2;
147    }
148
149    if ($offset != length $line)
150    {
151         print STDERR "packihx: checksum: got odd number of bytes... I hate that";
152         exit -1;
153    }
154
155    $cks &= 0xff;
156    if ($cks)
157    {
158         $cks = 0x100 - $cks;
159    }
160    return $cks;
161 }