fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
at master 449 lines 7.8 kB view raw
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}