ao-tools: Add ao-elftohex and .ihx symbol support
[fw/altos] / ao-tools / lib / ao-hex.c
index 85acc07f59537c1e624d6b0000fd1146cc190ce5..5cfc63c1347e999e70b7dd2da2e4c497b33925f1 100644 (file)
 #include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <string.h>
 #include "ao-hex.h"
+#include "ao-verbose.h"
 
 struct ao_hex_input {
        FILE    *file;
@@ -118,7 +120,7 @@ ao_hex_read_record(struct ao_hex_input *input)
 
        while (state != read_done) {
                c = getc(input->file);
-               if (c == EOF && state != read_white) {
+               if (c == EOF && state != read_white && state != read_marker) {
                        ao_hex_error(input, "Unexpected EOF");
                        goto bail;
                }
@@ -128,6 +130,8 @@ ao_hex_read_record(struct ao_hex_input *input)
                        input->line++;
                switch (state) {
                case read_marker:
+                       if (c == EOF)
+                               return NULL;
                        if (c != ':') {
                                ao_hex_error(input, "Missing ':'");
                                goto bail;
@@ -246,13 +250,20 @@ ao_hex_file_read(FILE *file, char *name)
        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)
-                       goto bail;
+               if (!record) {
+                       if (feof(input.file)) {
+                               done = 1;
+                               break;
+                       } else
+                               goto bail;
+               }
                if (hex->nrecord == srecord) {
                        srecord *= 2;
                        newhex = realloc(hex,
@@ -263,8 +274,6 @@ ao_hex_file_read(FILE *file, char *name)
                        hex = newhex;
                }
                hex->records[hex->nrecord++] = record;
-               if (record->type == AO_HEX_RECORD_EOF)
-                       done = 1;
        }
        return hex;
 
@@ -273,6 +282,92 @@ bail:
        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)
 {
@@ -286,6 +381,8 @@ ao_hex_image_create(struct ao_hex_file *hex)
 
        int length;
 
+       /* Find the address bounds of the file
+        */
        base = 0xffffffff;
        bound = 0x0;
        extended_addr = 0;
@@ -293,7 +390,7 @@ ao_hex_image_create(struct ao_hex_file *hex)
                uint32_t r_bound;
                record = hex->records[i];
                switch (record->type) {
-               case 0:
+               case AO_HEX_RECORD_NORMAL:
                        addr = extended_addr + record->address;
                        r_bound = addr + record->length;
                        if (addr < base)
@@ -301,20 +398,21 @@ ao_hex_image_create(struct ao_hex_file *hex)
                        if (r_bound > bound)
                                bound = r_bound;
                        break;
-               case 1:
+               case AO_HEX_RECORD_EOF:
                        break;
-               case 2:
+               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 4:
+               case AO_HEX_RECORD_EXTENDED_ADDRESS_8:
                        if (record->length != 2)
                                return NULL;
-                       extended_addr = ((record->data[0] << 8) | record->data[1]) << 16;
+                       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);
@@ -327,18 +425,20 @@ ao_hex_image_create(struct ao_hex_file *hex)
        for (i = 0; i < hex->nrecord; i++) {
                record = hex->records[i];
                switch (record->type) {
-               case 0:
+               case AO_HEX_RECORD_NORMAL:
                        addr = extended_addr + record->address;
                        offset = addr - base;
                        memcpy(image->data + offset, record->data, record->length);
                        break;
-               case 1:
+               case AO_HEX_RECORD_EOF:
                        break;
-               case 2:
+               case AO_HEX_RECORD_EXTENDED_ADDRESS_4:
                        extended_addr = ((record->data[0] << 8) | record->data[1]) << 4;
                        break;
-               case 4:
-                       extended_addr = ((record->data[0] << 8) | record->data[1]) << 16;
+               case AO_HEX_RECORD_EXTENDED_ADDRESS_8:
+                       extended_addr = (record->data[0] << 24) | (record->data[1] << 16);
+                       break;
+               case AO_HEX_RECORD_SYMBOL:
                        break;
                }
        }
@@ -362,23 +462,165 @@ ao_hex_image_equal(struct ao_hex_image *a, struct ao_hex_image *b)
 }
 
 struct ao_hex_image *
-ao_hex_load(char *filename)
+ao_hex_load(char *filename, struct ao_sym **symbols, int *num_symbolsp)
 {
-       FILE *file;
+       FILE                    *file;
        struct ao_hex_file      *hex_file;
-       struct ao_hex_image *hex_image;
+       struct ao_hex_image     *hex_image;
 
        file = fopen (filename, "r");
        if (!file)
-               return 0;
+               return NULL;
        
        hex_file = ao_hex_file_read(file, filename);
        fclose(file);
        if (!hex_file)
-               return 0;
+               return NULL;
        hex_image = ao_hex_image_create(hex_file);
        if (!hex_image)
-               return 0;
+               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(sizeof (struct ao_hex_record), 1);
+       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;
+}