1 /* Read ".HEX" files and store it in memory
2    Copyright (C) 2001, 2002, 2003, 2004, 2005
3    Craig Franklin
4 
5 This file is part of gputils.
6 
7 gputils is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 gputils is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with gputils; see the file COPYING.  If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21 
22 #include "stdhdr.h"
23 #include "libgputils.h"
24 
25 #define LINESIZ         520
26 
27 static char     linebuf[LINESIZ];
28 static char    *linept;
29 static uint8_t  checksum;
30 static FILE    *infile;
31 
32 /*------------------------------------------------------------------------------------------------*/
33 
34 /* Converts a single ASCII character into a number. */
35 
36 static uint8_t
_a2n(uint8_t Character)37 _a2n(uint8_t Character)
38 {
39   uint8_t number;
40 
41   if (Character <= '9') {
42     number = Character - '0';
43   }
44   else {
45     /* Convert lower case to upper. */
46     Character &= ~('a' - 'A');
47     number = Character - ('A' - 10);
48   }
49   return number;
50 }
51 
52 /*------------------------------------------------------------------------------------------------*/
53 
54 static uint8_t
_readbyte(void)55 _readbyte(void)
56 {
57   uint8_t number;
58 
59   linept++;
60   number  = _a2n(*linept) << 4;
61   linept++;
62   number |= _a2n(*linept);
63 
64   checksum += number;
65   return number;
66 }
67 
68 /*------------------------------------------------------------------------------------------------*/
69 
70 static uint16_t
_readword(void)71 _readword(void)
72 {
73   uint16_t number;
74 
75   number  = (uint16_t)_readbyte();
76   number |= (uint16_t)_readbyte() << 8;
77   return number;
78 }
79 
80 /*------------------------------------------------------------------------------------------------*/
81 
82 static uint16_t
_swapword(uint16_t Input)83 _swapword(uint16_t Input)
84 {
85   uint16_t number;
86 
87   number = ((Input & 0xFF) << 8) | ((Input & 0xFF00) >> 8);
88   return number;
89 }
90 
91 /*------------------------------------------------------------------------------------------------*/
92 
93 hex_data_t *
gp_readhex(const char * File_name,MemBlock_t * M)94 gp_readhex(const char *File_name, MemBlock_t *M)
95 {
96   hex_data_t   *info;
97   unsigned int  length;
98   unsigned int  address;
99   unsigned int  type;
100   uint8_t       byte;
101   unsigned int  i;
102   unsigned int  page;
103 
104   info = GP_Malloc(sizeof(*info));
105   info->hex_format = INHX8M;
106   info->size       = 0;
107   info->error      = false;
108 
109   /* Open the input file. */
110   if ((infile = fopen(File_name, "rt")) == NULL) {
111     perror(File_name);
112     exit(1);
113   }
114 
115   /* Go to the beginning of the file. */
116   fseek(infile, 0L, SEEK_SET);
117 
118   /* Set the line pointer to the beginning of the line buffer. */
119   linept = linebuf;
120 
121   /* Read a line of data from the file, if NULL stop. */
122   while (fgets(linept, LINESIZ, infile) != NULL)
123   {
124     /* Set the line pointer to the beginning of the line buffer. */
125     linept = linebuf;
126 
127     checksum = 0;
128 
129     /* Fetch the number of bytes. */
130     length = _readbyte();
131     if (length == 0) {
132       fclose(infile);
133       return info;
134     }
135 
136     /* Fetch the address. */
137     address = _swapword(_readword());
138 
139     if (info->hex_format == INHX16) {
140       address *= 2;
141       length  *= 2;
142     }
143 
144     /* Read the type of record. */
145     type = _readbyte();
146 
147     if (type == IHEX_RECTYPE_EXT_LIN_ADDR) {
148       if (info->hex_format == INHX16) {
149         printf("\nHex Format Error\n");
150         fclose(infile);
151         info->error = true;
152         return info;
153       }
154 
155       /* INHX32 segment line. */
156       page = ((_readbyte() << 8) + _readbyte()) << 16;
157       info->hex_format = INHX32;
158     }
159     else {
160       /* Read the data (skipping last byte if at odd address). */
161       for (i = 0; i < length; ++i) {
162 	byte = _readbyte();
163 
164 	if (info->hex_format == INHX16) {
165 	  gp_mem_b_put(M, page | ((address + i) ^ 1), byte, File_name, NULL);
166 	}
167 	else {
168 	  gp_mem_b_put(M, page | (address + i),       byte, File_name, NULL);
169 	}
170       }
171 
172       info->size += length;
173     }
174 
175     /* Read the checksum, data is thrown away. */
176     _readbyte();
177 
178     if (checksum != 0) {
179       if (info->hex_format == INHX8M) {
180         /*  First attempt at INHX8M failed, try INHX16. */
181         fseek(infile, 0L, SEEK_SET);
182         info->hex_format = INHX16;
183         info->size       = 0;
184         /* Data in i_memory is trash. */
185         gp_mem_i_free(M);
186         M = gp_mem_i_create();
187       }
188       else {
189         printf("\nChecksum Error\n");
190         fclose(infile);
191         info->error = true;
192         return info;
193       }
194     }
195 
196     /* Set the line pointer to the beginning of the line buffer. */
197     linept = linebuf;
198   }
199 
200   fclose(infile);
201 
202   return info;
203 }
204