/* * Copyright © 2008 Keith Packard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "ccdbg.h" #include #include struct hex_input { FILE *file; int line; char *name; }; enum hex_read_state { read_marker, read_length, read_address, read_type, read_data, read_checksum, read_newline, read_white, read_done, }; static void ccdbg_hex_error(struct hex_input *input, char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "Hex error %s:%d: ", input->name, input->line); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); va_end(ap); } static void ccdbg_hex_free(struct hex_record *record) { if (!record) return; free(record); } static struct hex_record * ccdbg_hex_alloc(uint8_t length) { struct hex_record *record; record = calloc(1, sizeof(struct hex_record) + length); record->length = length; return record; } static int ishex(char c) { return isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); } static int fromhex(char c) { if (isdigit(c)) return c - '0'; if ('a' <= c && c <= 'f') return c - 'a' + 10; if ('A' <= c && c <= 'F') return c - 'A' + 10; abort(); return 0; } static uint8_t ccdbg_hex_checksum(struct hex_record *record) { uint8_t checksum = 0; int i; checksum += record->length; checksum += record->address >> 8; checksum += record->address & 0xff; checksum += record->type; for (i = 0; i < record->length; i++) checksum += record->data[i]; return -checksum; } static struct hex_record * ccdbg_hex_read_record(struct hex_input *input) { struct hex_record *record = NULL; enum hex_read_state state = read_marker; char c; int nhexbytes; uint32_t hex; uint32_t ndata; uint8_t checksum; while (state != read_done) { c = getc(input->file); if (c == EOF && state != read_white) { ccdbg_hex_error(input, "Unexpected EOF"); goto bail; } if (c == ' ') continue; if (c == '\n') input->line++; switch (state) { case read_marker: if (c != ':') { ccdbg_hex_error(input, "Missing ':'"); goto bail; } state = read_length; nhexbytes = 2; hex = 0; break; case read_length: case read_address: case read_type: case read_data: case read_checksum: if (!ishex(c)) { ccdbg_hex_error(input, "Non-hex char '%c'", c); goto bail; } hex = hex << 4 | fromhex(c); --nhexbytes; if (nhexbytes != 0) break; switch (state) { case read_length: record = ccdbg_hex_alloc(hex); if (!record) { ccdbg_hex_error(input, "Out of memory"); goto bail; } state = read_address; nhexbytes = 4; break; case read_address: record->address = hex; state = read_type; nhexbytes = 2; break; case read_type: record->type = hex; state = read_data; nhexbytes = 2; ndata = 0; break; case read_data: record->data[ndata] = hex; ndata++; nhexbytes = 2; break; case read_checksum: record->checksum = hex; state = read_newline; break; default: break; } if (state == read_data) if (ndata == record->length) { nhexbytes = 2; state = read_checksum; } hex = 0; break; case read_newline: if (c != '\n' && c != '\r') { ccdbg_hex_error(input, "Missing newline"); goto bail; } state = read_white; break; case read_white: if (!isspace(c)) { if (c == '\n') input->line--; if (c != EOF) ungetc(c, input->file); state = read_done; } break; case read_done: break; } } checksum = ccdbg_hex_checksum(record); if (checksum != record->checksum) { ccdbg_hex_error(input, "Invalid checksum (read 0x%02x computed 0x%02x)\n", record->checksum, checksum); goto bail; } return record; bail: ccdbg_hex_free(record); return NULL; } void ccdbg_hex_file_free(struct hex_file *hex) { int i; if (!hex) return; for (i = 0; i < hex->nrecord; i++) ccdbg_hex_free(hex->records[i]); free(hex); } static int ccdbg_hex_record_compar(const void *av, const void *bv) { const struct hex_record *a = *(struct hex_record **) av; const struct hex_record *b = *(struct hex_record **) bv; return (int) a->address - (int) b->address; } struct hex_file * ccdbg_hex_file_read(FILE *file, char *name) { struct hex_input input; struct hex_file *hex = NULL, *newhex; struct hex_record *record; int srecord = 1; int done = 0; hex = calloc(sizeof (struct hex_file) + sizeof (struct hex_record *), 1); input.name = name; input.line = 1; input.file = file; while (!done) { record = ccdbg_hex_read_record(&input); if (!record) goto bail; if (hex->nrecord == srecord) { srecord *= 2; newhex = realloc(hex, sizeof (struct hex_file) + srecord * sizeof (struct hex_record *)); if (!newhex) goto bail; hex = newhex; } hex->records[hex->nrecord++] = record; if (record->type == HEX_RECORD_EOF) done = 1; } /* * Sort them into increasing addresses, except for EOF */ qsort(hex->records, hex->nrecord - 1, sizeof (struct hex_record *), ccdbg_hex_record_compar); return hex; bail: ccdbg_hex_file_free(hex); return NULL; } struct hex_image * ccdbg_hex_image_create(struct hex_file *hex) { struct hex_image *image; struct hex_record *first, *last, *record; int i; uint32_t base, bound; uint32_t offset; int length; first = hex->records[0]; last = hex->records[hex->nrecord - 2]; /* skip EOF */ base = (uint32_t) first->address; bound = (uint32_t) last->address + (uint32_t) last->length; length = bound - base; image = calloc(sizeof(struct hex_image) + length, 1); if (!image) return NULL; image->address = base; image->length = length; memset(image->data, 0xff, length); for (i = 0; i < hex->nrecord - 1; i++) { record = hex->records[i]; offset = record->address - base; memcpy(image->data + offset, record->data, record->length); } return image; } void ccdbg_hex_image_free(struct hex_image *image) { free(image); } int ccdbg_hex_image_equal(struct hex_image *a, struct hex_image *b) { if (a->length != b->length) return 0; if (memcmp(a->data, b->data, a->length) != 0) return 0; return 1; }