37f96ce1d5
Added PGZ loader and a utility to create PGZ files from Motorola SREC. Removed SREC loader.
364 lines
10 KiB
C
364 lines
10 KiB
C
/*
|
|
* A simple utility to convert Motorola SREC to the Foenix PGZ file format
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define MAX_BUFFER 128
|
|
|
|
enum {
|
|
STAT_ADDRESS_OVERFLOW = -3, /* The address field was too big for 24-bit format */
|
|
STAT_COUNT_OVERFLOW = -2, /* The count field was too big for 24-bit format */
|
|
STAT_GOOD = 0, /* The record is good and may be used */
|
|
STAT_NO_RECORD, /* The line parsed contained no record or an ignored record type */
|
|
STAT_EOF, /* There is no more data in the file */
|
|
STAT_BAD_ADDRESS, /* There was a parse error in the address field */
|
|
STAT_BAD_DATA, /* There was a parse error in the data field */
|
|
STAT_BAD_CHKSUM, /* There was a parse error in the checksum field */
|
|
STAT_MISMATCH_CHKSUM, /* The checksum does not match the line */
|
|
STAT_ERROR /* General file read error */
|
|
};
|
|
|
|
/*
|
|
* Structure to contain a record from the SREC file
|
|
*/
|
|
typedef struct s_srecord {
|
|
short status;
|
|
short type;
|
|
short count;
|
|
short binary_count;
|
|
long address;
|
|
char data[MAX_BUFFER];
|
|
short checksum;
|
|
} t_srecord, *p_srecord;
|
|
|
|
/* Size of the address and count fields: 0 = 24-bit, 1 = 32-bit */
|
|
short use_32bits = 0;
|
|
|
|
/*
|
|
* Convert a hex digit to a binary number
|
|
*/
|
|
short hex_convert(char c) {
|
|
if ((c >= '0') && (c <= '9')) {
|
|
return (c - '0');
|
|
} else if ((c >= 'a') && (c <= 'f')) {
|
|
return (c - 'a' + 10);
|
|
} else if ((c >= 'A') && (c <= 'F')) {
|
|
return (c - 'A' + 10);
|
|
} else {
|
|
/* There was a problem... return -1 as the result */
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert a string of hex digits to a binary number
|
|
*
|
|
* Inputs:
|
|
* raw = the source string
|
|
* count = the number of characters to parse
|
|
*
|
|
* Returns:
|
|
* the binary number read, -1 if there was an error
|
|
*/
|
|
long parse_hex(char * raw, short count) {
|
|
int i;
|
|
long result = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
short n = hex_convert(raw[i]);
|
|
if (n == -1) {
|
|
return -1;
|
|
} else {
|
|
result = result * 16 + n;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Parse the data field
|
|
*
|
|
* Inputs:
|
|
* raw = the source string of hex digits (must be the start of the data field)
|
|
* data = the buffer to fill with the binary data
|
|
* count = the number of chars to put into data
|
|
*/
|
|
short parse_data(char * raw, char * data, short count) {
|
|
int i;
|
|
for (i = 0; i < count; i++) {
|
|
char * digits = &raw[2*i];
|
|
long n = parse_hex(digits, 2);
|
|
if (n == -1) {
|
|
return -1;
|
|
} else {
|
|
data[i] = (char)(n & 0xff);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read a record from the input stream and fill it out
|
|
*
|
|
* Inputs:
|
|
* fd = FILE pointer for the SREC file to read
|
|
* r = pointer to the record structure to fill out
|
|
*/
|
|
void read_record(FILE * fd, p_srecord r) {
|
|
char buffer[MAX_BUFFER];
|
|
short n;
|
|
int i;
|
|
|
|
r->status = STAT_NO_RECORD;
|
|
char * result = fgets(buffer, MAX_BUFFER, fd);
|
|
if (result == 0) {;
|
|
r->status = STAT_EOF;
|
|
|
|
} else if (strlen(buffer) >= 10) {
|
|
if ((buffer[0] == 'S') && isdigit(buffer[1]) && (buffer[1] >= '0') && (buffer[1] <= '9')) {
|
|
r->status = STAT_GOOD;
|
|
r->type = buffer[1] - '0';
|
|
r->count = (short)parse_hex(&buffer[2], 2);
|
|
r->checksum = (short)parse_hex(&buffer[strlen(buffer)-2], 2);
|
|
|
|
for (i = 0; i < MAX_BUFFER; i++) {
|
|
r->data[i] = 0;
|
|
}
|
|
|
|
switch (r->type) {
|
|
case 1:
|
|
/* Data record, 16-bit address */
|
|
r->binary_count = r->count - 3;
|
|
r->address = parse_hex(&buffer[4], 4);
|
|
n = parse_data(&buffer[8], r->data, r->binary_count);
|
|
if (n == -1) {
|
|
r->status = STAT_BAD_DATA;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
/* Data record, 24-bit address */
|
|
r->binary_count = r->count - 4;
|
|
r->address = parse_hex(&buffer[4], 6);
|
|
n = parse_data(&buffer[10], r->data, r->binary_count);
|
|
if (n == -1) {
|
|
r->status = STAT_BAD_DATA;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
/* Data record, 32-bit address */
|
|
r->binary_count = r->count - 5;
|
|
r->address = parse_hex(&buffer[4], 8);
|
|
n = parse_data(&buffer[12], r->data, r->binary_count);
|
|
if (n == -1) {
|
|
r->status = STAT_BAD_DATA;
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
/* Start address, 32-bit */
|
|
r->address = parse_hex(&buffer[4], 8);
|
|
r->binary_count = 0;
|
|
break;
|
|
|
|
case 8:
|
|
/* Start address, 24-bit */
|
|
r->address = parse_hex(&buffer[4], 6);
|
|
r->binary_count = 0;
|
|
break;
|
|
|
|
case 9:
|
|
/* Start address, 16-bit */
|
|
r->address = parse_hex(&buffer[4], 4);
|
|
r->binary_count = 0;
|
|
break;
|
|
|
|
default:
|
|
/* Unsupported type, return null */
|
|
r->status = STAT_NO_RECORD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Format the record for binary output
|
|
*
|
|
* Inputs:
|
|
* out = FILE pointer for the output binary file
|
|
* r = record to be output
|
|
*/
|
|
short write_record(FILE * out, p_srecord r) {
|
|
int i;
|
|
char buffer[MAX_BUFFER];
|
|
short count_idx; /* offset to count field in binary output */
|
|
short data_idx; /* offset to data field in binary output */
|
|
|
|
fprintf(stderr, "{type=%d, addr=%08x, count=%08x}\n", r->type, (int)r->address, (int)r->binary_count);
|
|
|
|
if (use_32bits) {
|
|
count_idx = 4;
|
|
data_idx = 8;
|
|
} else {
|
|
count_idx = 3;
|
|
data_idx = 6;
|
|
}
|
|
|
|
/* Clear out the buffer */
|
|
for (i = 0; i < MAX_BUFFER; i++) {
|
|
buffer[i] = 0;
|
|
}
|
|
|
|
/* Set the address of the record */
|
|
if (use_32bits) {
|
|
buffer[3] = (char)((r->address >> 24) & 0xff);
|
|
} else if ((r->address >> 24) != 0) {
|
|
return STAT_ADDRESS_OVERFLOW;
|
|
}
|
|
buffer[2] = (char)((r->address >> 16) & 0xff);
|
|
buffer[1] = (char)((r->address >> 8) & 0xff);
|
|
buffer[0] = (char)((r->address) & 0xff);
|
|
|
|
/* Set the count of the record */
|
|
if (use_32bits) {
|
|
buffer[count_idx+3] = (char)((r->binary_count >> 24) & 0xff);
|
|
} else if ((r->binary_count >> 24) != 0) {
|
|
return STAT_COUNT_OVERFLOW;
|
|
}
|
|
buffer[count_idx+2] = (char)((r->binary_count >> 16) & 0xff);
|
|
buffer[count_idx+1] = (char)((r->binary_count >> 8) & 0xff);
|
|
buffer[count_idx] = (char)((r->binary_count) & 0xff);
|
|
|
|
/* Fill out the data */
|
|
for (i = 0; i < r->binary_count; i++) {
|
|
buffer[data_idx+i] = r->data[i];
|
|
}
|
|
|
|
int result = write(fileno(out), buffer, r->binary_count + data_idx);
|
|
if (result == -1) {
|
|
perror("Error writing file");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char * argv[]) {
|
|
FILE * in_file;
|
|
FILE * out_file;
|
|
short in_file_arg;
|
|
short out_file_arg;
|
|
char signature[1];
|
|
t_srecord r;
|
|
int line_number = 0;
|
|
int keep_going = 1;
|
|
short n;
|
|
|
|
switch (argc) {
|
|
case 3:
|
|
in_file_arg = 1;
|
|
out_file_arg = 2;
|
|
use_32bits = 0;
|
|
break;
|
|
|
|
case 4:
|
|
if (strcmp(argv[1], "--large") == 0) {
|
|
in_file_arg = 2;
|
|
out_file_arg = 3;
|
|
use_32bits = 1;
|
|
} else {
|
|
fprintf(stderr, "Usage: srecpgx [--large] <inputfile> <outputfile>\n");
|
|
exit(5);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Usage: srecpgx [--large] <inputfile> <outputfile>\n");
|
|
exit(5);
|
|
}
|
|
|
|
in_file = fopen(argv[in_file_arg], "r");
|
|
if (!in_file) {
|
|
fprintf(stderr, "Could not open input file (%d).\n", errno);
|
|
exit(6);
|
|
}
|
|
|
|
out_file = fopen(argv[out_file_arg], "w");
|
|
if (!out_file) {
|
|
perror("Could not open output file");
|
|
exit(7);
|
|
}
|
|
|
|
/* Write the signature... use a lower case 'z' to distinguish */
|
|
if (use_32bits) {
|
|
signature[0] = 'z';
|
|
} else {
|
|
signature[0] = 'Z';
|
|
}
|
|
if (write(fileno(out_file), signature, 1) == -1) {
|
|
perror("Error writing the output file");
|
|
exit(9);
|
|
}
|
|
|
|
while (keep_going) {
|
|
read_record(in_file, &r);
|
|
line_number++;
|
|
switch (r.status) {
|
|
case STAT_GOOD:
|
|
n = write_record(out_file, &r);
|
|
if (n == -1) {
|
|
fprintf(stderr, "Error writing the output file on line %d", line_number);
|
|
exit(10);
|
|
} else if (n == STAT_ADDRESS_OVERFLOW) {
|
|
fprintf(stderr, "Address too big for 24-bit binary on line %d", line_number);
|
|
exit(11);
|
|
} else if (n == STAT_COUNT_OVERFLOW) {
|
|
fprintf(stderr, "Count too big for 24-bit binary on line %d", line_number);
|
|
exit(12);
|
|
}
|
|
break;
|
|
|
|
case STAT_ERROR:
|
|
/* There was an error reading the file */
|
|
fprintf(stderr, "Error reading the input file on line %d.\n", line_number);
|
|
exit(8);
|
|
|
|
case STAT_EOF:
|
|
/* End of file... stop looping, close files, and quit */
|
|
fprintf(stderr, "d");
|
|
keep_going = 0;
|
|
break;
|
|
|
|
case STAT_BAD_ADDRESS:
|
|
fprintf(stderr, "Bad address on line %d.\n", line_number);
|
|
exit(1);
|
|
|
|
case STAT_BAD_DATA:
|
|
fprintf(stderr, "Bad data on line %d.\n", line_number);
|
|
exit(2);
|
|
|
|
case STAT_BAD_CHKSUM:
|
|
fprintf(stderr, "Bad checksum on line %d.\n", line_number);
|
|
exit(3);
|
|
|
|
case STAT_MISMATCH_CHKSUM:
|
|
fprintf(stderr, "Checksum did not match on line %d.\n", line_number);
|
|
exit(4);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose(in_file);
|
|
fclose(out_file);
|
|
return 0;
|
|
}
|