fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/lib/thex.c *
7 * Created: 2020-11-27 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2020-2021 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 "thex.h"
27
28
29typedef struct {
30 unsigned char type;
31 unsigned char size;
32 unsigned char data[256];
33 unsigned short cksum;
34} record_t;
35
36
37static
38int thex_skip_line (FILE *fp)
39{
40 int c;
41
42 while ((c = fgetc (fp)) != EOF) {
43 if ((c == 0x0a) || (c == 0x0d)) {
44 return (0);
45 }
46 }
47
48 return (1);
49}
50
51static
52int thex_get_uint8 (FILE *fp, unsigned char *val)
53{
54 unsigned i;
55 int c;
56
57 *val = 0;
58
59 for (i = 0; i < 2; i++) {
60 c = fgetc (fp);
61
62 if ((c >= '0') && (c <= '9')) {
63 *val = (*val << 4) | (c - '0');
64 }
65 else if ((c >= 'A') && (c <= 'F')) {
66 *val = (*val << 4) | (c - 'A' + 10);
67 }
68 else if ((c >= 'a') && (c <= 'f')) {
69 *val = (*val << 4) | (c - 'a' + 10);
70 }
71 else {
72 return (1);
73 }
74 }
75
76 return (0);
77}
78
79static
80unsigned thex_get_cksum (record_t *rec)
81{
82 unsigned i, c1, c2;
83
84 c1 = 0;
85 c2 = c1;
86 c1 += rec->type;
87 c2 += c1;
88 c1 += rec->size;
89 c2 += c1;
90
91 for (i = 0; i < rec->size; i++) {
92 c1 += rec->data[i];
93 c2 += c1;
94 }
95
96 c1 += c2;
97
98 return (((c2 & 0xff) << 8) | (c1 & 0xff));
99}
100
101static
102int thex_get_record (FILE *fp, record_t *rec)
103{
104 int c;
105 unsigned i;
106 unsigned char buf[2];
107
108 while (1) {
109 if ((c = fgetc (fp)) == EOF) {
110 return (1);
111 }
112
113 if (c == 'T') {
114 break;
115 }
116
117 if ((c != 0x0a) && (c != 0x0d)) {
118 thex_skip_line (fp);
119 }
120 }
121
122 if (thex_get_uint8 (fp, &rec->type)) {
123 return (1);
124 }
125
126 if (thex_get_uint8 (fp, &rec->size)) {
127 return (1);
128 }
129
130 for (i = 0; i < rec->size; i++) {
131 if (thex_get_uint8 (fp, rec->data + i)) {
132 return (1);
133 }
134 }
135
136 for (i = 0; i < 2; i++) {
137 if (thex_get_uint8 (fp, buf + i)) {
138 return (1);
139 }
140 }
141
142 rec->cksum = (buf[0] << 8) | buf[1];
143
144 thex_skip_line (fp);
145
146 return (0);
147}
148
149int thex_load_fp (FILE *fp, void *ext, thex_set_f set)
150{
151 unsigned i;
152 unsigned long seg, ofs, seg2, ofs2;
153 record_t rec;
154
155 seg = 0;
156 ofs = 0;
157
158 while (1) {
159 if (thex_get_record (fp, &rec)) {
160 break;
161 }
162
163 if (rec.cksum != thex_get_cksum (&rec)) {
164 return (1);
165 }
166
167 switch (rec.type) {
168 case 0xff: /* description */
169 break;
170
171 case 0xfe: /* end of file */
172 return (0);
173
174 case 0x00: /* data */
175 for (i = 0; i < rec.size; i++) {
176 set (ext, seg + ofs + i, rec.data[i]);
177 }
178 ofs += rec.size;
179 break;
180
181 case 0x01: /* set start address */
182 break;
183
184 case 0x02:
185 case 0x03:
186 if (rec.size < 2) {
187 return (1);
188 }
189
190 seg2 = 0;
191 ofs2 = (rec.data[0] << 8) | rec.data[1];
192 ofs &= 0xffff;
193
194 if (rec.type == 0x03) {
195 if ((seg != seg2) || (ofs != ofs2)) {
196 return (1);
197 }
198 }
199
200 seg = seg2;
201 ofs = ofs2;
202 break;
203
204 case 0x04:
205 case 0x05:
206 if (rec.size < 4) {
207 return (1);
208 }
209
210 seg2 = 0;
211 ofs2 = (rec.data[0] << 8) | rec.data[1];
212 ofs2 = (ofs2 << 8) | rec.data[2];
213 ofs2 = (ofs2 << 8) | rec.data[3];
214
215 if (rec.type == 0x05) {
216 if ((seg != seg2) || (ofs != ofs2)) {
217 return (1);
218 }
219 }
220
221 seg = seg2;
222 ofs = ofs2;
223 break;
224
225 case 0x06:
226 case 0x07:
227 if (rec.size < 4) {
228 return (1);
229 }
230
231 seg2 = (rec.data[0] << 8) | rec.data[1];
232 ofs2 = (rec.data[2] << 8) | rec.data[3];
233 seg2 <<= 4;
234 ofs &= 0xffff;
235
236 if (rec.type == 0x07) {
237 if ((seg != seg2) || (ofs != ofs2)) {
238 return (1);
239 }
240 }
241
242 seg = seg2;
243 ofs = ofs2;
244 break;
245
246 default:
247 return (1);
248 }
249 }
250
251 return (0);
252}
253
254int thex_load (const char *fname, void *ext, thex_set_f set)
255{
256 int r;
257 FILE *fp;
258
259 if ((fp = fopen (fname, "rb")) == NULL) {
260 return (1);
261 }
262
263 r = thex_load_fp (fp, ext, set);
264
265 fclose (fp);
266
267 return (r);
268}
269
270
271static
272void thex_put_uint8 (FILE *fp, unsigned char c)
273{
274 int tmp;
275
276 tmp = (c >> 4) & 0x0f;
277 tmp += (tmp <= 9) ? '0' : ('A' - 10);
278 fputc (tmp, fp);
279
280 tmp = c & 0x0f;
281 tmp += (tmp <= 9) ? '0' : ('A' - 10);
282 fputc (tmp, fp);
283}
284
285static
286int thex_put_record (FILE *fp, record_t *rec)
287{
288 unsigned i;
289
290 rec->cksum = thex_get_cksum (rec);
291
292 if (fputc ('T', fp) == EOF) {
293 return (1);
294 }
295
296 thex_put_uint8 (fp, rec->type);
297 thex_put_uint8 (fp, rec->size);
298
299 for (i = 0; i < rec->size; i++) {
300 thex_put_uint8 (fp, rec->data[i]);
301 }
302
303 thex_put_uint8 (fp, (rec->cksum >> 8) & 0xff);
304 thex_put_uint8 (fp, rec->cksum & 0xff);
305
306 if (fputc ('\n', fp) == EOF) {
307 return (1);
308 }
309
310 return (0);
311}
312
313static
314int thex_put_empty (FILE *fp, unsigned type)
315{
316 record_t rec;
317
318 rec.type = type;
319 rec.size = 0;
320
321 return (thex_put_record (fp, &rec));
322}
323
324static
325int thex_put_addr_seg (FILE *fp, unsigned seg, unsigned ofs, int check)
326{
327 record_t rec;
328
329 rec.type = 6 + (check != 0);
330 rec.size = 4;
331
332 rec.data[0] = (seg >> 8) & 0xff;
333 rec.data[1] = seg & 0xff;
334 rec.data[2] = (ofs >> 8) & 0xff;
335 rec.data[3] = ofs & 0xff;
336
337 return (thex_put_record (fp, &rec));
338}
339
340static
341int thex_put_addr (FILE *fp, unsigned long addr, int check)
342{
343 unsigned i;
344 record_t rec;
345
346 rec.type = (check != 0);
347
348 if (addr > 65535) {
349 rec.type += 4;
350 rec.size = 4;
351 }
352 else {
353 rec.type += 2;
354 rec.size = 2;
355 addr <<= 16;
356 }
357
358 for (i = 0; i < rec.size; i++) {
359 rec.data[i] = (addr >> 24) & 0xff;
360 addr <<= 8;
361 }
362
363 return (thex_put_record (fp, &rec));
364}
365
366int thex_save_start (FILE *fp)
367{
368 if (thex_put_empty (fp, 0xff)) {
369 return (1);
370 }
371
372 return (0);
373}
374
375int thex_save_seg (FILE *fp, unsigned seg, unsigned ofs, unsigned long size, void *ext, thex_get_f get)
376{
377 unsigned i;
378 unsigned long addr;
379 record_t rec;
380
381 addr = ofs;
382
383 if (thex_put_addr_seg (fp, seg, addr, 0)) {
384 return (1);
385 }
386
387 while (size > 0) {
388 rec.type = 0x00;
389 rec.size = (size < 16) ? size : 16;
390
391 for (i = 0; i < rec.size; i++) {
392 rec.data[i] = get (ext, ((unsigned long) seg << 4) + addr + i);
393 }
394
395 if (thex_put_record (fp, &rec)) {
396 return (1);
397 }
398
399 addr += rec.size;
400 size -= rec.size;
401 }
402
403 if (thex_put_addr_seg (fp, seg, addr, 1)) {
404 return (1);
405 }
406
407 return (0);
408}
409
410int thex_save (FILE *fp, unsigned long addr, unsigned long size, void *ext, thex_get_f get)
411{
412 unsigned i;
413 record_t rec;
414
415 if (thex_put_addr (fp, addr, 0)) {
416 return (1);
417 }
418
419 while (size > 0) {
420 rec.type = 0x00;
421 rec.size = (size < 16) ? size : 16;
422
423 for (i = 0; i < rec.size; i++) {
424 rec.data[i] = get (ext, addr + i);
425 }
426
427 if (thex_put_record (fp, &rec)) {
428 return (1);
429 }
430
431 addr += rec.size;
432 size -= rec.size;
433 }
434
435 if (thex_put_addr (fp, addr, 1)) {
436 return (1);
437 }
438
439 return (0);
440}
441
442int thex_save_done (FILE *fp)
443{
444 if (thex_put_empty (fp, 0xfe)) {
445 return (1);
446 }
447
448 return (0);
449}