Subversion Repositories svn.mios

Rev

Rev 840 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
628 adamjking 1
/*
2
 * @(#)HexFile.java beta8   2006/04/23
3
 *
4
 * Copyright (C) 2008    Jason Williams
5
 *
6
 * This application is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This application is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this application; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 */
20
 
21
package org.midibox.mios;
22
 
23
import java.io.BufferedReader;
24
import java.io.FileNotFoundException;
25
import java.io.FileReader;
26
import java.io.IOException;
27
import java.util.Arrays;
840 tk 28
import java.util.HashMap;
628 adamjking 29
import java.util.LinkedList;
30
 
31
public class HexFile {
32
 
33
    // /////////////////////////
34
    // Constants
35
    // /////////////////////////
36
 
37
    public static final int BLOCK_SIZE_DEFAULT = 256;
38
 
39
    // address mapping for MIOS8
40
    public static final int BLOCK_SIZE_FLASH = 256;
41
    public static final long HEX_FILE_FLASH_ADDRESS_START = 0x00000000;
42
    public static final long HEX_FILE_FLASH_ADDRESS_END = 0x0003FFFF;
43
    public static final long MAPPED_FLASH_ADDRESS_START = 0x00000000;
44
    public static final long MAPPED_FLASH_ADDRESS_END = 0x0003FFFF;
45
 
46
    public static final int BLOCK_SIZE_EEPROM = 256;
47
    public static final long HEX_FILE_EEPROM_ADDRESS_START = 0x00F00000;
48
    public static final long HEX_FILE_EEPROM_ADDRESS_END = 0x00F00FFF;
49
    public static final long MAPPED_EEPROM_ADDRESS_START = 0x00008000;
50
    public static final long MAPPED_EEPROM_ADDRESS_END = 0x00008FFF;
51
 
52
    public static final int BLOCK_SIZE_BANKSTICK = 256;
53
    public static final long HEX_FILE_BANKSTICK_ADDRESS_START = 0x00400000;
54
    public static final long HEX_FILE_BANKSTICK_ADDRESS_END = 0x0047FFFF;
55
    public static final long MAPPED_BANKSTICK_ADDRESS_START = 0x00010000;
56
    public static final long MAPPED_BANKSTICK_ADDRESS_END = 0x0001FFFF;
57
 
58
    // no address mapping for MIOS32
59
    public static final long MIOS32_IGNORED_BSL_RANGE_START = 0x08000000;
60
    public static final long MIOS32_IGNORED_BSL_RANGE_END = 0x08003FFF;
61
    // TODO: for STM32 - later we will have to request the target device via
62
    // SysEx request before the BSL range will be determined
63
 
64
    // /////////////////////////
65
    // Data Members
66
    // /////////////////////////
67
 
68
    //
69
    // Store a list of "big" blocks which are 256 bytes in size.
70
    // Blocks also have to be aligned on 256 byte boundaries.
71
    // We assume that MIOS and application code will be partitioned
72
    // along 256 byte boundaries.
73
    //
74
    // NB: Block bytes default to 0xFF.
75
    //
76
    private LinkedList _blocks;
77
 
840 tk 78
    // maps block address -> block index
79
    private HashMap _block_address_map;
80
 
628 adamjking 81
    // Used to keep track of the address offset records
82
    // in the file.
83
    private long _lBaseAddress;
84
 
85
    // Goes true when the EOF record is read in and parsed.
86
    private boolean _bEOF;
87
 
88
    // Stores any error during read().
89
    private String _sLastError;
90
 
91
    // /////////////////////////
92
    // Public interface
93
    // /////////////////////////
94
 
95
    public HexFile() {
96
        _clear();
97
    }
98
 
99
    public int getBlockCount() {
100
        return _blocks.size();
101
    }
102
 
103
    public Block getBlock(int n) {
104
        return (Block) _blocks.get(n);
105
    }
106
 
107
    public void dump() {
108
        for (int n = 0; n < _blocks.size(); n++) {
109
            Block block = (Block) _blocks.get(n);
110
            block.dump();
111
        }
112
    }
113
 
114
    public boolean read(String sFilename, boolean mios32) {
115
        _clear();
116
 
117
        boolean bOK = false;
118
        try {
119
            BufferedReader br = new BufferedReader(new FileReader(sFilename));
120
            String sLine;
121
            int nLineNumber = 1;
122
            while ((sLine = br.readLine()) != null) {
123
                bOK = _readLine(sLine, mios32);
124
                if (!bOK) {
125
                    // Stop reading file, there was an error.
126
                    break;
127
                }
128
                if (_bEOF) {
129
                    // Stop reading file, EOF was seen.
130
                    bOK = true;
131
                    break;
132
                }
133
                nLineNumber++;
134
            }
135
 
136
            br.close();
137
 
138
            if (!_bEOF) {
139
                bOK = false;
140
                // Not OK, we didn't reach EOF
141
                // If there's no error string already,
142
                // the error is "No EOF".
143
                if (_sLastError.length() == 0) {
144
                    _sLastError = "No EOF record in file.";
145
                } else {
146
                    _sLastError = "Error in line "
147
                            + Integer.toString(nLineNumber) + ": "
148
                            + _sLastError;
149
                }
150
                System.out.println(_sLastError);
151
            }
152
        } catch (FileNotFoundException e) {
153
            _sLastError = e.getMessage();
154
            System.out.println(e);
155
        } catch (IOException e) {
156
            _sLastError = e.getMessage();
157
            System.out.println(e);
158
        }
159
 
160
        return bOK;
161
    }
162
 
163
    // /////////////////////////
164
    // Private implemetation
165
    // /////////////////////////
166
 
167
    private boolean _readLine(String sLine, boolean mios32) {
168
        if (sLine.length() == 0) {
169
            return true;
170
        }
171
 
172
        // Line must start with a colon.
173
        if (sLine.charAt(0) != ':') {
174
            _sLastError = "Line did not start with a colon.";
175
            return false;
176
        }
177
 
178
        // Must have even number of hex digits after the colon.
179
        int nHexDigits = sLine.length() - 1;
180
        if (nHexDigits % 2 != 0) {
181
            _sLastError = "Line did not have an even number of hex digits after the colon.";
182
            return false;
183
        }
184
 
185
        int nBufferSize = nHexDigits / 2;
186
        // Must have at least 5 bytes.
187
        // 5 = data bytes (1) + address (2) + record type (1) + data (0..255) +
188
        // checksum (1)
189
        if (nBufferSize < 5) {
190
            _sLastError = "Line did not have at least 5 bytes.";
191
            return false;
192
        }
193
 
194
        byte[] axBuffer = new byte[nBufferSize];
195
        byte xCalcChecksum = 0;
196
        for (int i = 0; i < nBufferSize; i++) {
197
            Integer temp = new Integer(0);
198
            temp = Integer.decode("0x"
199
                    + sLine.substring((i * 2) + 1, (i * 2) + 3));
200
            axBuffer[i] = temp.byteValue();
201
            // If not last byte (which is the checksum byte)
202
            if (i < (nBufferSize - 1)) {
203
                xCalcChecksum = (byte) (((int) xCalcChecksum + (int) axBuffer[i]) % 256);
204
            }
205
        }
206
        xCalcChecksum = (byte) (((int) 0x100 - (int) xCalcChecksum) & 0xFF);
207
 
208
        int nDataBytes = axBuffer[0];
209
 
210
        // Check number of data bytes matches field in record.
211
        // 5 = data bytes (1) + address (2) + record type (1) + checksum (1)
212
        if (nDataBytes != (nBufferSize - 5)) {
213
            _sLastError = "Line did not have specified number of data bytes.";
214
            return false;
215
        }
216
        long lAddress = (axBuffer[1] & 0xFF) * 256 + (axBuffer[2] & 0xFF);
217
        byte xRecordType = axBuffer[3];
218
        byte[] axData = new byte[nDataBytes];
219
        for (int i = 4; i < 4 + nDataBytes; i++) {
220
            axData[i - 4] = axBuffer[i];
221
        }
222
        byte xChecksum = axBuffer[nBufferSize - 1];
223
 
224
        if (xChecksum != xCalcChecksum) {
225
            _sLastError = "Checksum did not match.";
226
            return false;
227
        }
228
 
229
        if (xRecordType == 0x00) {
230
            long lFullAddress = _lBaseAddress + lAddress;
231
 
232
            Block block = new Block();
233
 
234
            block.lAddress = lFullAddress;
235
            block.lAddressMapped = lFullAddress; // will be changed of memory
236
            // is mapped to other target
237
            // address
238
            block.axData = axData;
239
 
240
            _insertBlock(block, mios32);
241
        } else if (xRecordType == 0x01) {
242
            _bEOF = true;
243
            return true;
244
        } else if (xRecordType == 0x02) {
245
            // This is here for completeness.
246
            // This record type is unobserved in MPASM hex files.
247
            _lBaseAddress = (axBuffer[4] & 0xFF) * 256 + (axBuffer[5] & 0xFF);
248
            _lBaseAddress = _lBaseAddress << 4;
249
        } else if (xRecordType == 0x04) {
250
            _lBaseAddress = (axBuffer[4] & 0xFF) * 256 + (axBuffer[5] & 0xFF);
251
            _lBaseAddress = _lBaseAddress << 16;
252
        }
253
 
254
        return true;
255
    }
256
 
257
    private boolean _insertBlock(Block newBlock, boolean mios32) {
258
        // First work out which big block this newBlock belongs to.
259
        // Then find this big block in the list.
260
        // If it exists, copy newBlock into it.
261
        // If it doesn't exist, create it and then copy newBlock into it.
262
 
263
        // TK: it's possible that a line contains code for two blocks (unaligned
264
        // addresses)
265
        // therefore we have to insert the data stepwise
266
 
840 tk 267
        // Addendum1 (optimisation meassure to speed up hex upload)
850 adamjking 268
        // the effect described above has only be observed in .hex files
269
        // generated with GNU tools (-> MIOS32) - it's ensured that the
270
        // BSL block is never between two hex file lines, accordingly we
840 tk 271
        // can check for a valid block here before starting the insertion
628 adamjking 272
 
840 tk 273
        // Addendum2: block addresses are stored in a HashMap now
274
        // this speeds up hex load significantly! :-)
275
 
276
        long lByteAddress = newBlock.lAddress;
277
 
278
        int nBlockSize = BLOCK_SIZE_DEFAULT;
279
        long lMapAddressOffset = 0;
280
        if (mios32) {
281
            // MIOS32: ignore BSL range
282
            if (lByteAddress >= MIOS32_IGNORED_BSL_RANGE_START
850 adamjking 283
                    && lByteAddress <= MIOS32_IGNORED_BSL_RANGE_END) {
840 tk 284
                // We don't store the block.
285
                return true;
286
            }
287
        } else {
288
            // MIOS8: map EEPROM/BankStick address
289
            if (lByteAddress >= HEX_FILE_FLASH_ADDRESS_START
850 adamjking 290
                    && lByteAddress <= HEX_FILE_FLASH_ADDRESS_END) {
840 tk 291
                nBlockSize = BLOCK_SIZE_FLASH;
292
            } else if (lByteAddress >= HEX_FILE_EEPROM_ADDRESS_START
850 adamjking 293
                    && lByteAddress <= HEX_FILE_EEPROM_ADDRESS_END) {
840 tk 294
                nBlockSize = BLOCK_SIZE_EEPROM;
295
                lMapAddressOffset = MAPPED_EEPROM_ADDRESS_START
850 adamjking 296
                        - HEX_FILE_EEPROM_ADDRESS_START;
840 tk 297
            } else if (lByteAddress >= HEX_FILE_BANKSTICK_ADDRESS_START
850 adamjking 298
                    && lByteAddress <= HEX_FILE_BANKSTICK_ADDRESS_END) {
840 tk 299
                nBlockSize = BLOCK_SIZE_BANKSTICK;
300
                lMapAddressOffset = MAPPED_BANKSTICK_ADDRESS_START
850 adamjking 301
                        - HEX_FILE_BANKSTICK_ADDRESS_START;
628 adamjking 302
            } else {
840 tk 303
                // We don't store the block.
304
                return true;
628 adamjking 305
            }
840 tk 306
        }
628 adamjking 307
 
840 tk 308
        int nBytePos;
309
        for (nBytePos = 0; nBytePos < newBlock.axData.length; ++nBytePos, ++lByteAddress) {
310
            byte bData = newBlock.axData[nBytePos];
311
 
312
            Long lBigBlockAddress = lByteAddress / nBlockSize * nBlockSize;
313
 
850 adamjking 314
            Integer nBigBlockIndex = (Integer) _block_address_map
315
                    .get(lBigBlockAddress);
840 tk 316
            if (nBigBlockIndex == null) {
628 adamjking 317
                Block newBigBlock = new Block();
318
                newBigBlock.axData = new byte[nBlockSize];
319
                Arrays.fill(newBigBlock.axData, (byte) 0xFF);
320
                newBigBlock.lAddress = lBigBlockAddress;
321
                newBigBlock.lAddressMapped = lBigBlockAddress
322
                        + lMapAddressOffset;
323
 
840 tk 324
                // Append new big block.
325
                _blocks.add(newBigBlock);
326
                nBigBlockIndex = _blocks.size() - 1;
327
                _block_address_map.put(lBigBlockAddress, nBigBlockIndex);
628 adamjking 328
            }
329
 
330
            // Copy the data into the big block.
331
            Block bigBlock = (Block) _blocks.get(nBigBlockIndex);
332
            long lOffset = lByteAddress - bigBlock.lAddress;
333
 
334
            // System.out.println("#" + nBytePos + " Block " + lBigBlockAddress
335
            // + " Addr " + newBlock.lAddress + " Offset" + lOffset + ", len: "
336
            // + newBlock.axData.length);
337
 
338
            bigBlock.axData[(int) lOffset] = bData;
339
        }
340
 
341
        return true;
342
    }
343
 
344
    private void _clear() {
345
        _blocks = new LinkedList();
840 tk 346
        _block_address_map = new HashMap();
628 adamjking 347
        _lBaseAddress = 0;
348
        _bEOF = false;
349
        _sLastError = "";
350
    }
351
}