1 /* ".HEX" file output for gputils
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
3    James Bowman, Craig Franklin
4 
5     Copyright (C) 2015-2016 Molnar Karoly
6 
7 This file is part of gputils.
8 
9 gputils is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13 
14 gputils is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with gputils; see the file COPYING.  If not, write to
21 the Free Software Foundation, 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.  */
23 
24 #include "stdhdr.h"
25 #include "libgputils.h"
26 
27 /* mode flags */
28 enum mode_flags_e {
29   HMODE_ALL,
30   HMODE_LOW,
31   HMODE_HIGH,
32   HMODE_SWAP  /* swap bytes for INHX16 format */
33 };
34 
35 static int         checksum;
36 static const char *newline;
37 static FILE       *hex;
38 static MemBlock_t *memory;
39 
40 /*------------------------------------------------------------------------------------------------*/
41 
42 static void
_new_record(void)43 _new_record(void)
44 {
45   fprintf(hex, ":");
46   checksum = 0;
47 }
48 
49 /*------------------------------------------------------------------------------------------------*/
50 
51 static void
_write_byte(uint8_t Value)52 _write_byte(uint8_t Value)
53 {
54   checksum += (int)Value;
55   fprintf(hex, "%02X", Value);
56 }
57 
58 /*------------------------------------------------------------------------------------------------*/
59 
60 /* Write big-endian word. */
61 
62 static void
_write_bg_word(uint16_t Word)63 _write_bg_word(uint16_t Word)
64 {
65   _write_byte((Word >> 8) & 0xff);
66   _write_byte(Word & 0xff);
67 }
68 
69 /*------------------------------------------------------------------------------------------------*/
70 
71 /* Write little-endian word. */
72 
73 static void
_write_word(uint16_t Word)74 _write_word(uint16_t Word)
75 {
76   _write_byte(Word & 0xff);
77   _write_byte((Word >> 8) & 0xff);
78 }
79 
80 /*------------------------------------------------------------------------------------------------*/
81 
82 static void
_start_record(unsigned int Start,unsigned int Len)83 _start_record(unsigned int Start, unsigned int Len)
84 {
85   _new_record();
86   _write_byte(Len);
87   _write_bg_word(Start);
88   _write_byte(0);
89 }
90 
91 /*------------------------------------------------------------------------------------------------*/
92 
93 static void
_end_record(void)94 _end_record(void)
95 {
96   _write_byte((-checksum) & 0xff);
97   fprintf(hex, "%s", newline);
98 }
99 
100 /*------------------------------------------------------------------------------------------------*/
101 
102 static void
_data_line(unsigned int Start,unsigned int Stop,enum mode_flags_e Mode)103 _data_line(unsigned int Start, unsigned int Stop, enum mode_flags_e Mode)
104 {
105   uint8_t byte;
106 
107   if (Mode == HMODE_ALL) {
108     _start_record(Start, Stop - Start);
109     while (Start < Stop) {
110       if (!gp_mem_b_get(memory, Start++, &byte, NULL, NULL)) {
111         byte = 0xff;
112       }
113       _write_byte(byte);
114     }
115   }
116   else if (Mode == HMODE_SWAP) {
117     /* MPLINK 2.40, 3.80, 4.11 and 4.3x do not support INHX16 format.
118      * (FIXME I have no idea where it comes from or if it can be used
119      * for whatever purpose it was written for.) */
120     assert(((Start % 2) == 0) && ((Stop % 2) == 0));
121     _start_record(Start / 2, (Stop  - Start) / 2);
122     while (Start < Stop) {
123       if (!gp_mem_b_get(memory, (Start++) ^ 1, &byte, NULL, NULL)) {
124         byte = 0xff;
125       }
126       _write_byte(byte);
127     }
128   }
129   else {
130     _start_record(Start, (Stop - Start) / (((Mode == HMODE_LOW) || (Mode == HMODE_HIGH)) ? 2 : 1));
131 
132     if (Mode == HMODE_HIGH) {
133       ++Start;
134     }
135 
136     while (Start < Stop) {
137       if (!gp_mem_b_get(memory, Start, &byte, NULL, NULL)) {
138         byte = 0xff;
139       }
140       _write_byte(byte);
141       Start += 2;
142     }
143   }
144   _end_record();
145 }
146 
147 /*------------------------------------------------------------------------------------------------*/
148 
149 static void
_seg_address_line(unsigned int Segment)150 _seg_address_line(unsigned int Segment)
151 {
152   _new_record();
153   _write_byte(2);
154   _write_word(0);
155   _write_byte(4);
156   _write_bg_word(Segment);
157   _end_record();
158 }
159 
160 /*------------------------------------------------------------------------------------------------*/
161 
162 static void
_last_line(void)163 _last_line(void)
164 {
165   _new_record();
166   _write_byte(0);
167   _write_word(0);
168   _write_byte(1);
169   _end_record();
170 }
171 
172 /*------------------------------------------------------------------------------------------------*/
173 
174 static void
_write_i_mem(enum formats Hex_format,enum mode_flags_e Mode,unsigned int Core_mask)175 _write_i_mem(enum formats Hex_format, enum mode_flags_e Mode, unsigned int Core_mask)
176 {
177   MemBlock_t   *m;
178   int           i;
179   int           j;
180   int           maximum;
181   uint8_t       byte;
182 
183   m = memory;
184   while (m != NULL) {
185     i = IMemAddrFromBase(m->base);
186 
187     maximum = i + I_MEM_MAX;
188 
189     if (Hex_format == INHX32) {
190       /* FIXME would mode swap require division by 2? */
191       _seg_address_line(m->base);
192     }
193     else {
194       assert(m->base == 0);
195     }
196 
197     while (i < maximum) {
198       if (!gp_mem_b_get(memory, i, &byte, NULL, NULL)) {
199         ++i;
200       }
201       else {
202         j = i;
203         while (gp_mem_b_get(memory, i, &byte, NULL, NULL)) {
204           ++i;
205           if ((((Mode == HMODE_ALL) || (Mode == HMODE_SWAP)) && ((i & 0xf) == 0)) || ((i & 0x1f) == 0)) {
206             break;
207           }
208         }
209 #if 0
210         /* disabled for MPASM compatibility,
211          * regression test gpasm.mchip/asmfiles/szee16.asm */
212         if (Core_mask > 0xFF) {
213           /* Write complete instructions, so move start down and stop up to even address. */
214           if (j & 1) {
215             --j;
216           }
217 
218           if (i & 1) {
219             ++i;
220           }
221         }
222 #endif
223         /* Now we have a run of (i - j) occupied memory locations. */
224         /* Write the data to the file */
225         /* To be bug-for-bug compatible with MPASM 5.34 we ignore negative addresses. */
226         if (j >= 0) {
227           _data_line(j, i, Mode);
228         }
229       }
230     }
231     m = m->next;
232   }
233 
234   _last_line();
235 }
236 
237 /*------------------------------------------------------------------------------------------------*/
238 
239 gp_boolean
gp_writehex(const char * Base_filename,MemBlock_t * M,enum formats Hex_format,int Num_errors,gp_boolean Dos_newlines,unsigned int Core_mask)240 gp_writehex(const char *Base_filename, MemBlock_t *M, enum formats Hex_format, int Num_errors,
241             gp_boolean Dos_newlines, unsigned int Core_mask)
242 {
243   char hex_filename[BUFSIZ];
244   char low_hex[BUFSIZ];
245   char high_hex[BUFSIZ];
246 
247   memory = M;
248 
249   newline = Dos_newlines ? "\r\n" : "\n";
250 
251    /* build file names */
252   snprintf(hex_filename, sizeof(hex_filename), "%s.hex", Base_filename);
253   snprintf(low_hex, sizeof(low_hex), "%s.hxl", Base_filename);
254   snprintf(high_hex, sizeof(high_hex), "%s.hxh", Base_filename);
255 
256   if (Num_errors > 0) {
257     /* Remove the hex files (if any). */
258     unlink(hex_filename);
259     unlink(low_hex);
260     unlink(high_hex);
261     return false;
262   }
263 
264   /* No error: overwrite the hex file */
265   if (Hex_format == INHX8S) {
266     /* Write the low memory */
267     hex = fopen(low_hex, "wt");
268 
269     if (hex == NULL) {
270       perror(low_hex);
271       return false;
272     }
273 
274     _write_i_mem(Hex_format, HMODE_LOW, Core_mask);
275     fclose(hex);
276 
277     /* Write the high memory. */
278     hex = fopen(high_hex, "wt");
279 
280     if (hex == NULL) {
281       perror(high_hex);
282       return false;
283     }
284 
285     _write_i_mem(Hex_format, HMODE_HIGH, Core_mask);
286     fclose(hex);
287   }
288   else if (Hex_format == INHX16) {
289     hex = fopen(hex_filename, "wt");
290 
291     if (hex == NULL) {
292       perror(hex_filename);
293       return false;
294     }
295 
296     _write_i_mem(Hex_format, HMODE_SWAP, Core_mask);
297     fclose(hex);
298   }
299   else {
300     hex = fopen(hex_filename, "wt");
301 
302     if (hex == NULL) {
303       perror(hex_filename);
304       return false;
305     }
306 
307     _write_i_mem(Hex_format, HMODE_ALL, Core_mask);
308     fclose(hex);
309   }
310 
311   return true;
312 }
313 
314 /*------------------------------------------------------------------------------------------------*/
315 
316 /* Scan the memory to see if it exceeds 32kB limit on INHX8M limit. */
317 
318 gp_boolean
gp_writehex_check(MemBlock_t * M,enum formats Hex_format)319 gp_writehex_check(MemBlock_t *M, enum formats Hex_format)
320 {
321   gp_boolean ok = true;
322 
323   if (Hex_format != INHX32) {
324     while (M != NULL) {
325       if (M->base > 0) {
326         ok = false;
327         /* Superfluous to watch the further blocks. */
328         break;
329       }
330       M = M->next;
331     }
332   }
333 
334   return ok;
335 }
336