fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/lib/srec.c *
7 * Created: 2005-03-28 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2005-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#include <string.h>
26
27#include "srec.h"
28
29
30typedef struct {
31 unsigned char type;
32 unsigned char cnt;
33 unsigned long addr;
34 unsigned asize;
35 unsigned char data[256];
36 unsigned char cksum;
37} record_t;
38
39
40static unsigned srec_asize[16] = {
41 2, 2, 3, 4, 2, 2, 2, 4,
42 3, 2, 2, 2, 2, 2, 2, 2
43};
44
45
46static
47int srec_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 srec_get_hex4 (FILE *fp, unsigned char *val)
68{
69 int c;
70
71 c = fgetc (fp);
72
73 if ((c >= '0') && (c <= '9')) {
74 *val = c - '0';
75 }
76 else if ((c >= 'A') && (c <= 'F')) {
77 *val = c - 'A' + 10;
78 }
79 else if ((c >= 'a') && (c <= 'f')) {
80 *val = c - 'a' + 10;
81 }
82 else {
83 return (1);
84 }
85
86 return (0);
87}
88
89static
90int srec_get_hex8 (FILE *fp, unsigned char *val)
91{
92 unsigned char v1, v2;
93
94 if (srec_get_hex4 (fp, &v1) || srec_get_hex4 (fp, &v2)) {
95 return (1);
96 }
97
98 *val = ((v1 & 0x0f) << 4) | (v2 & 0x0f);
99
100 return (0);
101}
102
103static
104void srec_set_hex4 (FILE *fp, unsigned val)
105{
106 val &= 0x0f;
107 val += (val <= 9) ? '0' : ('A' - 10);
108
109 fputc (val, fp);
110}
111
112static
113void srec_set_hex8 (FILE *fp, unsigned val)
114{
115 srec_set_hex4 (fp, (val >> 4) & 0x0f);
116 srec_set_hex4 (fp, val & 0x0f);
117}
118
119static
120unsigned char srec_get_cksum (record_t *rec)
121{
122 unsigned i;
123 unsigned char ck;
124
125 ck = (rec->cnt + rec->asize + 1) & 0xff;
126 ck += rec->addr & 0xff;
127 ck += (rec->addr >> 8) & 0xff;
128 ck += (rec->addr >> 16) & 0xff;
129 ck += (rec->addr >> 24) & 0xff;
130
131 for (i = 0; i < rec->cnt; i++) {
132 ck += rec->data[i];
133 }
134
135 ck = ~ck & 0xff;
136
137 return (ck);
138}
139
140static
141void srec_init_record (record_t *rec, unsigned type, unsigned cnt, unsigned long addr)
142{
143 rec->type = type;
144 rec->cnt = cnt;
145 rec->addr = addr;
146
147 if (type < 16) {
148 rec->asize = srec_asize[type];
149 }
150 else {
151 rec->asize = 2;
152 }
153}
154
155static
156int srec_get_record (FILE *fp, record_t *rec)
157{
158 unsigned i;
159 int c;
160 unsigned char addr;
161 unsigned char type;
162
163 while (1) {
164 c = fgetc (fp);
165
166 if (c == EOF) {
167 return (1);
168 }
169
170 if (c == 'S') {
171 if (srec_get_hex4 (fp, &type) == 0) {
172 break;
173 }
174 }
175
176 if ((c != 0x0a) && (c != 0x0d)) {
177 srec_skip_line (fp);
178 }
179 }
180
181 rec->type = type;
182 rec->asize = srec_asize[type];
183
184 if (srec_get_hex8 (fp, &rec->cnt)) {
185 return (1);
186 }
187
188 if (rec->cnt < (rec->asize + 1)) {
189 return (1);
190 }
191
192 rec->cnt -= rec->asize + 1;
193 rec->addr = 0;
194
195 for (i = 0; i < rec->asize; i++) {
196 if (srec_get_hex8 (fp, &addr)) {
197 return (1);
198 }
199 rec->addr = (rec->addr << 8) | (addr & 0xff);
200 }
201
202 for (i = 0; i < rec->cnt; i++) {
203 if (srec_get_hex8 (fp, &rec->data[i])) {
204 return (1);
205 }
206 }
207
208 if (srec_get_hex8 (fp, &rec->cksum)) {
209 return (1);
210 }
211
212 srec_skip_line (fp);
213
214 return (0);
215}
216
217static
218void srec_set_record (FILE *fp, record_t *rec)
219{
220 unsigned i;
221
222 rec->cksum = srec_get_cksum (rec);
223
224 fputc ('S', fp);
225
226 srec_set_hex4 (fp, rec->type);
227 srec_set_hex8 (fp, rec->cnt + rec->asize + 1);
228
229 for (i = 0; i < rec->asize; i++) {
230 srec_set_hex8 (fp,
231 (rec->addr >> (8 * (rec->asize - i - 1))) & 0xff
232 );
233 }
234
235 for (i = 0; i < rec->cnt; i++) {
236 srec_set_hex8 (fp, rec->data[i]);
237 }
238
239 srec_set_hex8 (fp, rec->cksum);
240
241 fputs ("\n", fp);
242}
243
244static
245void srec_set_hdr (FILE *fp, const char *name)
246{
247 unsigned cnt;
248 record_t rec;
249
250 cnt = strlen (name);
251
252 if (cnt > 20) {
253 cnt = 20;
254 }
255
256 srec_init_record (&rec, 0, cnt, 0);
257
258 memcpy (rec.data, name, cnt);
259
260 srec_set_record (fp, &rec);
261}
262
263static
264void srec_set_end (FILE *fp, unsigned long saddr)
265{
266 record_t rec;
267
268 if (saddr & 0xff000000) {
269 srec_init_record (&rec, 7, 0, saddr);
270 }
271 else if (saddr & 0xffff0000) {
272 srec_init_record (&rec, 8, 0, saddr);
273 }
274 else {
275 srec_init_record (&rec, 9, 0, saddr);
276 }
277
278 srec_set_record (fp, &rec);
279}
280
281int srec_load_fp (FILE *fp, void *ext, srec_set_f set)
282{
283 unsigned i;
284 record_t rec;
285
286 while (1) {
287 if (srec_get_record (fp, &rec)) {
288 return (0);
289 }
290
291 if (rec.cksum != srec_get_cksum (&rec)) {
292 return (1);
293 }
294
295 if (rec.type == 0) {
296 /* header record */
297 ;
298 }
299 else if ((rec.type == 1) || (rec.type == 2) || (rec.type == 3)) {
300 for (i = 0; i < rec.cnt; i++) {
301 set (ext, rec.addr + i, rec.data[i]);
302 }
303 }
304 else if (rec.type == 5) {
305 /* record count record */
306 ;
307 }
308 else if ((rec.type == 7) || (rec.type == 8) || (rec.type == 9)) {
309 /* end record */
310 return (0);
311 }
312 }
313
314 return (0);
315}
316
317int srec_load (const char *fname, void *ext, srec_set_f set)
318{
319 int r;
320 FILE *fp;
321
322 fp = fopen (fname, "rb");
323 if (fp == NULL) {
324 return (1);
325 }
326
327 r = srec_load_fp (fp, ext, set);
328
329 fclose (fp);
330
331 return (r);
332}
333
334int srec_save_start (FILE *fp, const char *name)
335{
336 srec_set_hdr (fp, name);
337
338 return (0);
339}
340
341int srec_save (FILE *fp, unsigned long base, unsigned long size, void *ext, srec_get_f get)
342{
343 unsigned i;
344 record_t rec;
345
346 while (size > 0) {
347 rec.cnt = (size < 16) ? size : 16;
348
349 if (base & 0xff000000) {
350 rec.type = 3;
351 rec.asize = 4;
352 }
353 else if (base & 0xffff0000) {
354 rec.type = 2;
355 rec.asize = 3;
356 }
357 else {
358 rec.type = 1;
359 rec.asize = 2;
360 }
361
362 rec.addr = base & 0xffffffff;
363
364 for (i = 0; i < rec.cnt; i++) {
365 rec.data[i] = get (ext, rec.addr + i);
366 }
367
368 srec_set_record (fp, &rec);
369
370 base += rec.cnt;
371 size -= rec.cnt;
372 }
373
374 return (0);
375}
376
377int srec_save_done (FILE *fp)
378{
379 srec_set_end (fp, 0);
380
381 return (0);
382}