fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/lib/ihex.c *
7 * Created: 2004-06-23 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2004-2013 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <stdlib.h>
24#include <stdio.h>
25
26#include "ihex.h"
27
28
29typedef struct {
30 unsigned char type;
31 unsigned char cnt;
32 unsigned short addr;
33 unsigned char data[256];
34 unsigned char cksum;
35} record_t;
36
37
38#define IHEX_REC_DATA 0x00
39#define IHEX_REC_EOFR 0x01
40#define IHEX_REC_ESAR 0x02
41#define IHEX_REC_SSAR 0x03
42#define IHEX_REC_ELAR 0x04
43#define IHEX_REC_SLAR 0x05
44
45
46static
47int ihex_skip_line (FILE *fp)
48{
49 int c;
50
51 while (1) {
52 c = fgetc (fp);
53
54 if (c == EOF) {
55 return (1);
56 }
57
58 if ((c == 0x0a) || (c == 0x0d)) {
59 return (0);
60 }
61 }
62
63 return (0);
64}
65
66static
67int ihex_get_hex8 (FILE *fp, unsigned char *val)
68{
69 unsigned i;
70 int c;
71
72 *val = 0;
73
74 for (i = 0; i < 2; i++) {
75 c = fgetc (fp);
76
77 if ((c >= '0') && (c <= '9')) {
78 *val = (*val << 4) | (c - '0');
79 }
80 else if ((c >= 'A') && (c <= 'F')) {
81 *val = (*val << 4) | (c - 'A' + 10);
82 }
83 else if ((c >= 'a') && (c <= 'f')) {
84 *val = (*val << 4) | (c - 'a' + 10);
85 }
86 else {
87 return (1);
88 }
89 }
90
91 return (0);
92}
93
94static
95unsigned char ihex_get_cksum (record_t *rec)
96{
97 unsigned i;
98 unsigned char ck;
99
100 ck = rec->cnt & 0xff;
101 ck += rec->addr & 0xff;
102 ck += (rec->addr >> 8) & 0xff;
103 ck += rec->type & 0xff;
104
105 for (i = 0; i < rec->cnt; i++) {
106 ck += rec->data[i];
107 }
108
109 ck = (~ck + 1) & 0xff;
110
111 return (ck);
112}
113
114static
115int ihex_get_record (FILE *fp, record_t *rec)
116{
117 unsigned i;
118 int c;
119 unsigned char a1, a2;
120
121 while (1) {
122 c = fgetc (fp);
123
124 if (c == EOF) {
125 return (1);
126 }
127
128 if (c == ':') {
129 break;
130 }
131
132 if ((c != 0x0a) && (c != 0x0d)) {
133 ihex_skip_line (fp);
134 }
135 }
136
137 if (ihex_get_hex8 (fp, &rec->cnt)) {
138 return (1);
139 }
140
141 if (ihex_get_hex8 (fp, &a1) || ihex_get_hex8 (fp, &a2)) {
142 return (1);
143 }
144
145 rec->addr = (a1 << 8) | a2;
146
147 if (ihex_get_hex8 (fp, &rec->type)) {
148 return (1);
149 }
150
151 for (i = 0; i < rec->cnt; i++) {
152 if (ihex_get_hex8 (fp, &rec->data[i])) {
153 return (1);
154 }
155 }
156
157 if (ihex_get_hex8 (fp, &rec->cksum)) {
158 return (1);
159 }
160
161 ihex_skip_line (fp);
162
163 return (0);
164}
165
166
167static
168void ihex_set_hex8 (FILE *fp, unsigned char c)
169{
170 int tmp;
171
172 tmp = (c >> 4) & 0x0f;
173 tmp += (tmp <= 9) ? '0' : ('A' - 10);
174 fputc (tmp, fp);
175
176 tmp = c & 0x0f;
177 tmp += (tmp <= 9) ? '0' : ('A' - 10);
178 fputc (tmp, fp);
179}
180
181static
182void ihex_set_record (FILE *fp, record_t *rec)
183{
184 unsigned i;
185
186 rec->cksum = ihex_get_cksum (rec);
187
188 fprintf (fp, ":%02X%04X%02X",
189 (unsigned) rec->cnt, (unsigned) rec->addr & 0xffffU, (unsigned) rec->type
190 );
191
192 for (i = 0; i < rec->cnt; i++) {
193 ihex_set_hex8 (fp, rec->data[i]);
194 }
195
196 ihex_set_hex8 (fp, rec->cksum);
197
198 fputs ("\n", fp);
199}
200
201static
202void ihex_set_esar (FILE *fp, unsigned seg)
203{
204 record_t rec;
205
206 rec.type = IHEX_REC_ESAR;
207 rec.cnt = 2;
208 rec.addr = 0;
209 rec.data[0] = (seg >> 8) & 0xff;
210 rec.data[1] = seg & 0xff;
211
212 ihex_set_record (fp, &rec);
213}
214
215static
216void ihex_set_ulba (FILE *fp, unsigned long addr)
217{
218 record_t rec;
219
220 rec.type = 0x04;
221 rec.cnt = 2;
222 rec.addr = 0;
223 rec.data[0] = (addr >> 24) & 0xff;
224 rec.data[1] = (addr >> 16) & 0xff;
225
226 ihex_set_record (fp, &rec);
227}
228
229static
230void ihex_set_end (FILE *fp)
231{
232 record_t rec;
233
234 rec.type = 0x01;
235 rec.cnt = 0;
236 rec.addr = 0;
237
238 ihex_set_record (fp, &rec);
239}
240
241int ihex_load_fp (FILE *fp, void *ext, ihex_set_f set)
242{
243 unsigned i;
244 unsigned mode;
245 unsigned long addr, ulba;
246 record_t rec;
247
248 mode = 0;
249 ulba = 0;
250
251 while (1) {
252 if (ihex_get_record (fp, &rec)) {
253 break;
254 }
255
256 if (rec.cksum != ihex_get_cksum (&rec)) {
257 return (1);
258 }
259
260 if (rec.type == IHEX_REC_EOFR) {
261 return (0);
262 }
263 else if (rec.type == IHEX_REC_ELAR) {
264 if (rec.cnt == 2) {
265 mode = 0;
266 ulba = (rec.data[0] << 8) | rec.data[1];
267 ulba = ulba << 16;
268 }
269 else {
270 return (1);
271 }
272 }
273 else if (rec.type == IHEX_REC_ESAR) {
274 if (rec.cnt == 2) {
275 mode = 1;
276 ulba = (rec.data[0] << 8) | rec.data[1];
277 ulba = ulba << 4;
278 }
279 else {
280 return (1);
281 }
282 }
283 else if (rec.type == IHEX_REC_DATA) {
284 addr = ulba + (rec.addr & 0xffffU);
285
286 for (i = 0; i < rec.cnt; i++) {
287 if (mode == 0) {
288 addr = ulba + rec.addr + i;
289 }
290 else {
291 addr = ulba + ((rec.addr + i) & 0xffffU);
292 }
293
294 set (ext, addr, rec.data[i]);
295 }
296 }
297 }
298
299 return (0);
300}
301
302int ihex_load (const char *fname, void *ext, ihex_set_f set)
303{
304 int r;
305 FILE *fp;
306
307 fp = fopen (fname, "rb");
308 if (fp == NULL) {
309 return (1);
310 }
311
312 r = ihex_load_fp (fp, ext, set);
313
314 fclose (fp);
315
316 return (r);
317}
318
319
320int ihex_save (FILE *fp, unsigned seg, unsigned ofs, unsigned long size, void *ext, ihex_get_f get)
321{
322 unsigned i;
323 unsigned cnt;
324 unsigned short oldseg;
325 record_t rec;
326
327 oldseg = 0;
328
329 while (size > 0) {
330 if (seg != oldseg) {
331 ihex_set_esar (fp, seg);
332 oldseg = seg;
333 }
334
335 cnt = (size < 16) ? size : 16;
336
337 if (((ofs + cnt) & 0xffff) < ofs) {
338 cnt = -ofs & 0xffff;
339 }
340
341 rec.type = 0x00;
342 rec.addr = ofs;
343 rec.cnt = cnt;
344
345 for (i = 0; i < rec.cnt; i++) {
346 rec.data[i] = get (ext, ((unsigned long) seg << 4) + ofs);
347
348 ofs = (ofs + 1) & 0xffff;
349 }
350
351 ihex_set_record (fp, &rec);
352
353 if (ofs == 0) {
354 seg += 0x1000;
355 }
356
357 size -= rec.cnt;
358 }
359
360 return (0);
361}
362
363int ihex_save_linear (FILE *fp, unsigned long base, unsigned long size, void *ext, ihex_get_f get)
364{
365 unsigned i;
366 unsigned long ulba, addr;
367 record_t rec;
368
369 ulba = 0;
370 addr = base;
371
372 while (size > 0) {
373 rec.type = 0x00;
374 rec.addr = addr & 0xffffU;
375 rec.cnt = (size < 16) ? size : 16;
376
377 for (i = 0; i < rec.cnt; i++) {
378 rec.data[i] = get (ext, addr + i);
379 }
380
381 if ((ulba & 0xffff0000UL) != (addr & 0xffff0000UL)) {
382 ihex_set_ulba (fp, addr);
383 ulba = addr & 0xffff0000UL;
384 }
385
386 ihex_set_record (fp, &rec);
387
388 addr += rec.cnt;
389 size -= rec.cnt;
390 }
391
392 return (0);
393}
394
395int ihex_save_done (FILE *fp)
396{
397 ihex_set_end (fp);
398 return (0);
399}