/* * 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 #include #include #include #include #include #include "ao-hex.h" #include "ao-verbose.h" struct ao_hex_input { FILE *file; int line; char *name; }; enum ao_hex_read_state { read_marker, read_length, read_address, read_type, read_data, read_checksum, read_newline, read_white, read_done, }; static void ao_hex_error(struct ao_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 ao_hex_free(struct ao_hex_record *record) { if (!record) return; free(record); } static struct ao_hex_record * ao_hex_alloc(uint8_t length) { struct ao_hex_record *record; record = calloc(1, sizeof(struct ao_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 ao_hex_checksum(struct ao_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 ao_hex_record * ao_hex_read_record(struct ao_hex_input *input) { struct ao_hex_record *record = NULL; enum ao_hex_read_state state = read_marker; char c; int nhexbytes = 0; uint32_t hex = 0; uint32_t ndata = 0; uint8_t checksum; while (state != read_done) { c = getc(input->file); if (c == EOF && state != read_white && state != read_marker) { ao_hex_error(input, "Unexpected EOF"); goto bail; } if (c == ' ') continue; if (c == '\n') input->line++; switch (state) { case read_marker: if (c == EOF) return NULL; if (c != ':') { ao_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)) { ao_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 = ao_hex_alloc(hex); if (!record) { ao_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') { ao_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 = ao_hex_checksum(record); if (checksum != record->checksum) { ao_hex_error(input, "Invalid checksum (read 0x%02x computed 0x%02x)\n", record->checksum, checksum); goto bail; } return record; bail: ao_hex_free(record); return NULL; } void ao_hex_file_free(struct ao_hex_file *hex) { int i; if (!hex) return; for (i = 0; i < hex->nrecord; i++) ao_hex_free(hex->records[i]); free(hex); } struct ao_hex_file * ao_hex_file_read(FILE *file, char *name) { struct ao_hex_input input; struct ao_hex_file *hex = NULL, *newhex; struct ao_hex_record *record; int srecord = 1; int done = 0; hex = calloc(sizeof (struct ao_hex_file) + sizeof (struct ao_hex_record *), 1); if (!hex) return NULL; input.name = name; input.line = 1; input.file = file; while (!done) { record = ao_hex_read_record(&input); if (!record) { if (feof(input.file)) { done = 1; break; } else goto bail; } if (hex->nrecord == srecord) { srecord *= 2; newhex = realloc(hex, sizeof (struct ao_hex_file) + srecord * sizeof (struct ao_hex_record *)); if (!newhex) goto bail; hex = newhex; } hex->records[hex->nrecord++] = record; } return hex; bail: ao_hex_file_free(hex); return NULL; } static struct ao_sym * load_symbols(struct ao_hex_file *hex, int *num_symbolsp) { uint32_t extended_addr; uint32_t addr; int i; struct ao_hex_record *record; struct ao_sym *symbols = NULL; struct ao_sym *symbol; int num_symbols = 0; int size_symbols = 0; extended_addr = 0; for (i = 0; i < hex->nrecord; i++) { record = hex->records[i]; switch (record->type) { case AO_HEX_RECORD_NORMAL: addr = extended_addr + record->address; break; case AO_HEX_RECORD_EOF: break; case AO_HEX_RECORD_EXTENDED_ADDRESS_4: if (record->length != 2) goto bail; extended_addr = ((record->data[0] << 8) | record->data[1]) << 4; break; case AO_HEX_RECORD_EXTENDED_ADDRESS_8: if (record->length != 2) goto bail; extended_addr = (record->data[0] << 24) | (record->data[1] << 16); break; case AO_HEX_RECORD_SYMBOL: addr = extended_addr + record->address; if (num_symbols == size_symbols) { struct ao_sym *new_symbols; int new_size; if (!size_symbols) new_size = 16; else new_size = size_symbols * 2; new_symbols = realloc(symbols, new_size * sizeof (struct ao_sym)); if (!new_symbols) goto bail; symbols = new_symbols; size_symbols = new_size; } symbol = &symbols[num_symbols]; memset(symbol, 0, sizeof (struct ao_sym)); symbol->name = calloc(record->length + 1, 1); if (!symbol->name) goto bail; memcpy(symbol->name, record->data, record->length); symbol->addr = addr; ao_printf(AO_VERBOSE_EXE, "Add symbol %s: %08x\n", symbol->name, symbol->addr); num_symbols++; break; } } *num_symbolsp = num_symbols; return symbols; bail: for (i = 0; i < num_symbols; i++) free(symbols[i].name); free(symbols); return NULL; } static void ao_hex_record_set_checksum(struct ao_hex_record *record) { uint8_t cksum = 0; int i; cksum += record->length; cksum += record->address >> 8; cksum += record->address; cksum += record->type; for (i = 0; i < record->length; i++) cksum += record->data[i]; record->checksum = -cksum; } struct ao_hex_image * ao_hex_image_create(struct ao_hex_file *hex) { struct ao_hex_image *image; struct ao_hex_record *record; int i; uint32_t addr; uint32_t base, bound; uint32_t offset; uint32_t extended_addr; int length; /* Find the address bounds of the file */ base = 0xffffffff; bound = 0x0; extended_addr = 0; for (i = 0; i < hex->nrecord; i++) { uint32_t r_bound; record = hex->records[i]; switch (record->type) { case AO_HEX_RECORD_NORMAL: addr = extended_addr + record->address; r_bound = addr + record->length; if (addr < base) base = addr; if (r_bound > bound) bound = r_bound; break; case AO_HEX_RECORD_EOF: break; case AO_HEX_RECORD_EXTENDED_ADDRESS_4: if (record->length != 2) return NULL; extended_addr = ((record->data[0] << 8) | record->data[1]) << 4; break; case AO_HEX_RECORD_EXTENDED_ADDRESS_8: if (record->length != 2) return NULL; extended_addr = (record->data[0] << 24) | (record->data[1] << 16); break; case AO_HEX_RECORD_SYMBOL: break; } } length = bound - base; image = calloc(sizeof(struct ao_hex_image) + length, 1); if (!image) return NULL; image->address = base; image->length = length; memset(image->data, 0xff, length); extended_addr = 0; for (i = 0; i < hex->nrecord; i++) { record = hex->records[i]; switch (record->type) { case AO_HEX_RECORD_NORMAL: addr = extended_addr + record->address; offset = addr - base; memcpy(image->data + offset, record->data, record->length); break; case AO_HEX_RECORD_EOF: break; case AO_HEX_RECORD_EXTENDED_ADDRESS_4: extended_addr = ((record->data[0] << 8) | record->data[1]) << 4; break; case AO_HEX_RECORD_EXTENDED_ADDRESS_8: extended_addr = (record->data[0] << 24) | (record->data[1] << 16); break; case AO_HEX_RECORD_SYMBOL: break; } } return image; } void ao_hex_image_free(struct ao_hex_image *image) { free(image); } static uint32_t min(uint32_t a, uint32_t b) { return a < b ? a : b; } static uint32_t max(uint32_t a, uint32_t b) { return a > b ? a : b; } struct ao_hex_image * ao_hex_image_cat(struct ao_hex_image *a, struct ao_hex_image *b) { struct ao_hex_image *n; uint32_t base, bound; uint32_t length; base = min(a->address, b->address); bound = max(a->address + a->length, b->address + b->length); length = bound - base; n = calloc (sizeof (struct ao_hex_image) + length, 1); if (!n) return NULL; n->address = base; n->length = length; memset(n->data, 0xff, length); memcpy(n->data + a->address - n->address, a->data, a->length); memcpy(n->data + b->address - n->address, b->data, b->length); return n; } int ao_hex_image_equal(struct ao_hex_image *a, struct ao_hex_image *b) { if (a->length != b->length) return 0; if (memcmp(a->data, b->data, a->length) != 0) return 0; return 1; } struct ao_hex_image * ao_hex_load(char *filename, struct ao_sym **symbols, int *num_symbolsp) { FILE *file; struct ao_hex_file *hex_file; struct ao_hex_image *hex_image; file = fopen (filename, "r"); if (!file) return NULL; hex_file = ao_hex_file_read(file, filename); fclose(file); if (!hex_file) return NULL; hex_image = ao_hex_image_create(hex_file); if (!hex_image) return NULL; if (symbols) *symbols = load_symbols(hex_file, num_symbolsp); ao_hex_file_free(hex_file); return hex_image; } #define BYTES_PER_RECORD 32 static struct ao_hex_file * ao_hex_file_create(struct ao_hex_image *image, struct ao_sym *symbols, int num_symbols) { /* split data into n-byte-sized chunks */ uint32_t data_records = (image->length + BYTES_PER_RECORD-1) / BYTES_PER_RECORD; /* extended address and data for each block, EOF, address and data for each symbol */ uint32_t total_records = data_records * 2 + 1 + num_symbols * 2; uint32_t offset; uint32_t address; uint32_t length; char *name; struct ao_hex_file *hex_file; int nrecord = 0; int s; struct ao_hex_record *record; hex_file = calloc(sizeof (struct ao_hex_file) + sizeof (struct ao_hex_record *) * total_records, 1); if (!hex_file) return NULL; /* Add the data */ for (offset = 0; offset < image->length; offset += BYTES_PER_RECORD) { uint32_t address = image->address + offset; uint32_t length = image->length - offset; if (length > BYTES_PER_RECORD) length = BYTES_PER_RECORD; record = calloc(sizeof (struct ao_hex_record) + 2, 1); record->type = AO_HEX_RECORD_EXTENDED_ADDRESS_8; record->address = 0; record->length = 2; record->data[0] = address >> 24; record->data[1] = address >> 16; ao_hex_record_set_checksum(record); hex_file->records[nrecord++] = record; record = calloc(sizeof (struct ao_hex_record) + length, 1); record->type = AO_HEX_RECORD_NORMAL; record->address = address; record->length = length; memcpy(record->data, image->data + offset, length); ao_hex_record_set_checksum(record); hex_file->records[nrecord++] = record; } /* Stick an EOF after the data */ record = calloc(1,sizeof (struct ao_hex_record) + 2); record->type = AO_HEX_RECORD_EOF; record->address = 0; record->length = 0; record->data[0] = 0; record->data[1] = 0; ao_hex_record_set_checksum(record); hex_file->records[nrecord++] = record; /* Add the symbols */ for (s = 0; s < num_symbols; s++) { name = symbols[s].name; address = symbols[s].addr; length = strlen (name); record = calloc(sizeof (struct ao_hex_record) + 2, 1); record->type = AO_HEX_RECORD_EXTENDED_ADDRESS_8; record->address = 0; record->length = 2; record->data[0] = address >> 24; record->data[1] = address >> 16; ao_hex_record_set_checksum(record); hex_file->records[nrecord++] = record; record = calloc(sizeof (struct ao_hex_record) + length, 1); record->type = AO_HEX_RECORD_SYMBOL; record->address = address; record->length = length; memcpy(record->data, name, length); ao_hex_record_set_checksum(record); hex_file->records[nrecord++] = record; } hex_file->nrecord = nrecord; return hex_file; } static bool ao_hex_write_record(FILE *file, struct ao_hex_record *record) { int i; fputc(':', file); fprintf(file, "%02x", record->length); fprintf(file, "%04x", record->address); fprintf(file, "%02x", record->type); for (i = 0; i < record->length; i++) fprintf(file, "%02x", record->data[i]); fprintf(file, "%02x", record->checksum); fputc('\n', file); return true; } bool ao_hex_save(FILE *file, struct ao_hex_image *image, struct ao_sym *symbols, int num_symbols) { struct ao_hex_file *hex_file; int i; bool ret = false; hex_file = ao_hex_file_create(image, symbols, num_symbols); if (!hex_file) goto create_failed; for (i = 0; i < hex_file->nrecord; i++) { if (!ao_hex_write_record(file, hex_file->records[i])) goto write_failed; } ret = true; if (fflush(file) != 0) ret = false; write_failed: ao_hex_file_free(hex_file); create_failed: return ret; }