source: ISPProgrammer/UIntelHexFile.pas

Last change on this file was 363, checked in by chronos, 12 years ago
  • Added: Package ISPProgrammer for in-system programming of various chips. Supports Dallas ISP protocol, Presto, Rabbit RFU and some others Atmel devices.
File size: 9.2 KB
Line 
1unit UIntelHexFile;
2
3interface
4
5uses
6 Classes, SysUtils, Contnrs, UCommon, UStreamHelper,
7 DateUtils;
8
9type
10 EFileCorrupted = class(Exception);
11 EChecksumError = class(Exception);
12
13 TIntelHexRecordType = (rtData, rtEndOfFile, rtSegmentAddress, rtExtendedAddress);
14
15 { TMappedMemory }
16
17 TMappedMemory = class(TStreamHelper)
18 BaseAddress: Integer;
19 procedure Assign(Source: TMappedMemory);
20 end;
21
22 { TIntelHexFile }
23
24 TIntelHexFile = class
25 private
26 function GetSize:Integer;
27 procedure WriteBlock(Block: TMappedMemory; StringList:TStringList);
28 function TwoComplement(Value: Byte): Byte;
29 public
30 Blocks: TObjectList; // TListObject<TMappedMemory>
31 BytePerLine: Byte;
32 procedure Assign(Source: TIntelHexFile);
33 function EqualTo(Source: TIntelHexFile): Boolean;
34 procedure LoadFromFile(FileName: string);
35 procedure SaveToFile(FileName: string);
36 procedure LoadFromStringList(StringList:TStringList);
37 procedure SaveToStringList(StringList:TStringList);
38 procedure LoadFromBinFile(FileName: string);
39 procedure SaveToBinFile(FileName: string);
40 procedure WriteContinuousBlock(AStream: TStream);
41 constructor Create;
42 destructor Destroy; override;
43 property Size: Integer read GetSize;
44 end;
45
46
47implementation
48
49resourcestring
50 SCorruptedFile = 'File corrupted "%s: %s".';
51 SChecksumError = 'Checksum error "%s: %s".';
52
53{ TMappedMemory }
54
55procedure TMappedMemory.Assign(Source: TMappedMemory);
56var
57 Buffer: array[0..10000] of Byte;
58 Count: Integer;
59begin
60 BaseAddress := Source.BaseAddress;
61 Endianness := Source.Endianness;
62 Source.Stream.Position := 0;
63 Stream.Size := 0;
64 repeat
65 Count := Source.Stream.Read(PByte(Buffer)^, SizeOf(Buffer));
66 Stream.Write(PByte(Buffer)^, Count);
67 until Source.Stream.Position >= Source.Stream.Size;
68end;
69
70{ TIntelHexFile }
71
72function TIntelHexFile.GetSize:Integer;
73var
74 I: Integer;
75begin
76 Result := 0;
77 for I := 0 to Blocks.Count - 1 do
78 Result := Result + TMappedMemory(Blocks[I]).Size;
79end;
80
81procedure TIntelHexFile.WriteBlock(Block:TMappedMemory; StringList:TStringList)
82 ;
83var
84 I: Integer;
85 L: Integer;
86 CheckSum: Byte;
87 OutputLine: string;
88 Count: Integer;
89 Line: TStreamHelper;
90begin
91 try
92 Line := TStreamHelper.Create;
93 Block.Position := 0;
94 for L := 0 to (Block.Size div BytePerLine) do begin
95 Count := BytePerLine;
96 if Count > (Block.Size - Block.Position) then
97 Count := Block.Size - Block.Position;
98 if Count > 0 then begin
99 Line.Size := 4 + Int64(Count);
100 Line.Position := 0;
101 Line.WriteByte(Count);
102 Line.WriteByte(((Block.Position + Block.BaseAddress) shr 8) and $ff);
103 Line.WriteByte((Block.Position + Block.BaseAddress) and $ff);
104 Line.WriteByte(Integer(rtData));
105 Line.WriteStreamPart(TStream(Block), Count);
106
107 CheckSum := TwoComplement(Line.Sum);
108 Line.WriteByte(CheckSum);
109
110 OutputLine := ':';
111 Line.Position := 0;
112 for I := 0 to Line.Size - 1 do
113 OutputLine := OutputLine + IntToHex(Line.ReadByte, 2);
114 StringList.Add(OutputLine);
115 end;
116 end;
117 finally
118 Line.Free;
119 end;
120end;
121
122function TIntelHexFile.TwoComplement(Value:Byte):Byte;
123begin
124 Result := (not Value + 1) and $ff;
125end;
126
127procedure TIntelHexFile.Assign(Source: TIntelHexFile);
128var
129 I: Integer;
130 NewBlock: TMappedMemory;
131begin
132 BytePerLine := Source.BytePerLine;
133 Blocks.Clear;
134 for I := 0 to Source.Blocks.Count - 1 do begin
135 NewBlock := TMappedMemory.Create;
136 NewBlock.Assign(TMappedMemory(Source.Blocks[I]));
137 Blocks.Add(NewBlock);
138 end;
139end;
140
141function TIntelHexFile.EqualTo(Source: TIntelHexFile): Boolean;
142var
143 I: Integer;
144 NewBlock: TMappedMemory;
145begin
146 Result := True;
147 if Source.Blocks.Count <> Blocks.Count then begin
148 Result := False;
149 Exit;
150 end;
151 for I := 0 to Source.Blocks.Count - 1 do begin
152 if not TMappedMemory(Blocks[I]).EqualTo(TMappedMemory(Source.Blocks[I])) then begin
153 Result := False;
154 Exit;
155 end;
156 end;
157end;
158
159procedure TIntelHexFile.LoadFromFile(FileName: string);
160var
161 StringList: TStringList;
162begin
163 try
164 StringList := TStringList.Create;
165 StringList.LoadFromFile(UTF8Decode(FileName));
166 LoadFromStringList(StringList);
167 finally
168 StringList.Free;
169 end;
170end;
171
172procedure TIntelHexFile.LoadFromStringList(StringList: TStringList);
173var
174 Row: string;
175 DataCount: Byte;
176 RecordType: TIntelHexRecordType;
177 I: Integer;
178 CheckSum: Byte;
179 LastAddress: Integer;
180 Address: Integer;
181 SegmentAddress: Word;
182 ExtendedAddress: Word;
183 Block: TMappedMemory;
184 Line: TStreamHelper;
185begin
186 try
187 Line := TStreamHelper.Create;
188 Blocks.Clear;
189 Block := nil;
190 LastAddress := -1;
191 ExtendedAddress := 0;
192 SegmentAddress := 0;
193 for I := 0 to StringList.Count - 1 do begin
194 Row := Trim(StringList[I]);
195 if Row = '' then Continue;
196 if SplitString(Row, 1) <> ':' then
197 raise EFileCorrupted.CreateFmt(SCorruptedFile, [IntToStr(I), StringList[I]]);
198
199 Line.Position := 0;
200 Line.Size := Length(Row) div 2;
201 while Length(Row) > 0 do
202 Line.WriteByte(StrToInt('$' + SplitString(Row, 2)));
203
204 // Read CheckSum
205 Line.Position := Line.Size - 1;
206 CheckSum := Line.ReadByte;
207 Line.Size := Line.Size - 1;
208 if CheckSum <> TwoComplement(Line.Sum) then
209 raise EChecksumError.CreateFmt(SChecksumError, [IntToStr(I), StringList[I]]);
210
211 Line.Position := 0;
212 DataCount := Line.ReadByte;
213 Address := ((Line.ReadByte shl 8) or Line.ReadByte) +
214 + (SegmentAddress shl 4) + (ExtendedAddress shl 16);
215
216 if LastAddress <> Address then begin
217 Block := TMappedMemory.Create;
218 Block.BaseAddress := Address;
219 Blocks.Add(Block);
220 end;
221 RecordType := TIntelHexRecordType(Line.ReadByte);
222 case RecordType of
223 rtData: begin
224 Block.WriteStreamPart(Line, Line.Size - Line.Position);
225 end;
226 rtEndOfFile: begin
227 Break;
228 end;
229 rtSegmentAddress: begin
230 SegmentAddress := (Line.ReadByte shl 8) or Line.ReadByte;
231 ExtendedAddress := 0;
232 end;
233 rtExtendedAddress: begin
234 ExtendedAddress := (Line.ReadByte shl 8) or Line.ReadByte;
235 SegmentAddress := 0;
236 end;
237 end;
238 LastAddress := Address + DataCount;
239 end;
240 finally
241 Line.Free;
242 end;
243end;
244
245procedure TIntelHexFile.SaveToFile(FileName: string);
246var
247 StringList: TStringList;
248begin
249 try
250 StringList := TStringList.Create;
251 SaveToStringList(StringList);
252 StringList.SaveToFile(UTF8Decode(FileName));
253 finally
254 StringList.Free;
255 end;
256end;
257
258procedure TIntelHexFile.SaveToStringList(StringList: TStringList);
259var
260 I: Integer;
261 Line: TStreamHelper;
262 OutputLine: string;
263 CheckSum: Byte;
264begin
265 try
266 StringList.Clear;
267
268 for I := 0 to Blocks.Count - 1 do
269 WriteBlock(TMappedMemory(Blocks[I]), StringList);
270
271 Line := TStreamHelper.Create;
272
273 // Write EndOfFile
274 Line.Size := 4;
275 Line.WriteByte(0);
276 Line.WriteByte(0);
277 Line.WriteByte(0);
278 Line.WriteByte(Integer(rtEndOfFile));
279
280 CheckSum := TwoComplement(Line.Sum);
281
282 Line.Position := 0;
283 OutputLine := ':';
284 for I := 0 to Line.Size - 1 do
285 OutputLine := OutputLine + IntToHex(Line.ReadByte, 2);
286 OutputLine := OutputLine + IntToHex(CheckSum, 2);
287 StringList.Add(OutputLine);
288
289 finally
290 Line.Free;
291 end;
292end;
293
294procedure TIntelHexFile.LoadFromBinFile(FileName:string);
295var
296 NewBlock: TMappedMemory;
297begin
298 // TODO: analyze empty areas with FF bytes and split them to blocks
299 Blocks.Clear;
300 NewBlock := TMappedMemory.Create(TMemoryStream.Create);
301 (NewBlock.Stream as TMemoryStream).LoadFromFile(UTF8Decode(FileName));
302 NewBlock.BaseAddress := 0;
303 Blocks.Add(NewBlock);
304end;
305
306procedure TIntelHexFile.SaveToBinFile(FileName:string);
307var
308 MergedMemory: TStreamHelper;
309 I: Integer;
310begin
311 try
312 MergedMemory := TStreamHelper.Create;
313
314 // Fill unused space by unused data (FF)
315 for I := 0 to Blocks.Count - 1 do
316 if MergedMemory.Size < (TMappedMemory(Blocks[I]).BaseAddress + TMappedMemory(Blocks[I]).Size) then
317 MergedMemory.Size := (TMappedMemory(Blocks[I]).BaseAddress + TMappedMemory(Blocks[I]).Size);
318 MergedMemory.FillByte($ff, MergedMemory.Size);
319
320 // Write all memory blocks
321 for I := 0 to Blocks.Count - 1 do begin
322 MergedMemory.Position := TMappedMemory(Blocks[I]).BaseAddress;
323 MergedMemory.WriteStream(TMappedMemory(Blocks[I]), TMappedMemory(Blocks[I]).Size);
324 end;
325 (MergedMemory.Stream as TMemoryStream).SaveToFile(UTF8Decode(FileName));
326 finally
327 MergedMemory.Free;
328 end;
329end;
330
331procedure TIntelHexFile.WriteContinuousBlock(AStream: TStream);
332var
333 I: Integer;
334begin
335 AStream.Size := 0;
336 for I := 0 to Blocks.Count - 1 do
337 with TMappedMemory(Blocks[I]) do begin
338 Position := 0;
339 AStream.Position := BaseAddress;
340 ReadStreamPart(AStream, Size);
341 end;
342end;
343
344constructor TIntelHexFile.Create;
345begin
346 Blocks := TObjectList.Create;
347 BytePerLine := 16;
348end;
349
350destructor TIntelHexFile.Destroy;
351begin
352 Blocks.Free;
353 inherited Destroy;
354end;
355
356end.
Note: See TracBrowser for help on using the repository browser.