diff options
Diffstat (limited to 'xex.c')
-rw-r--r-- | xex.c | 313 |
1 files changed, 313 insertions, 0 deletions
@@ -0,0 +1,313 @@ +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include "xex.h" + +int xex_errno; +static int xex_sys_errno; + +int xex_verbose = 0; + +static char *errors[] = { + "OK", /* XERR_NONE 0 */ + "Failed system call", /* XERR_SYSCALL 1 */ + "End address < start address", /* XERR_REVERSED 2 */ + "Truncated segment", /* XERR_TRUNCATED 3 */ + "No data", /* XERR_NULLDATA 4 */ + "Unknown/Invalid XEX error code", /* XERR_MAXERR 5 */ +}; + + +int xex_new_seg(xex_segment *seg, unsigned char *data) { + int offset = 0; + + if(data[0] == 0xff && data[1] == 0xff) { + offset = 2; + seg->has_ff_header = 1; + } else { + seg->has_ff_header = 0; + } + + seg->start_addr = data[offset] + (data[offset + 1] << 8); + offset += 2; + + seg->end_addr = data[offset] + (data[offset + 1] << 8); + offset += 2; + + seg->object = &data[offset]; + + seg->len = seg->end_addr - seg->start_addr + 1; + + return xex_check_seg(seg); +} + +int xex_init_seg(xex_segment *seg, unsigned char *object, unsigned short addr) { + object[0] = XEX_LSB(addr); + object[1] = XEX_MSB(addr); + + seg->start_addr = XEX_INITAD; + seg->end_addr = XEX_INITAD + 1; + seg->object = object; + seg->len = 2; + seg->has_ff_header = 0; + + if(xex_verbose) { + fprintf(stderr, "xex_init_seg(): created init segment @ %04X:\n", addr); + xex_print_seg_info(seg); + } + + return 1; +} + +int xex_run_seg(xex_segment *seg, unsigned char *object, unsigned short addr) { + object[0] = XEX_LSB(addr); + object[1] = XEX_MSB(addr); + + seg->start_addr = XEX_RUNAD; + seg->end_addr = XEX_RUNAD + 1; + seg->object = object; + seg->len = 2; + seg->has_ff_header = 0; + + if(xex_verbose) { + fprintf(stderr, "xex_run(): created run segment @ %04X:\n", addr); + xex_print_seg_info(seg); + } + + return 1; +} + +static int read_char(FILE *file) { + int c; + + c = getc(file); + if(c == EOF) { + if(ferror(file)) { + xex_sys_errno = errno; + xex_errno = XERR_SYSCALL; + } else { + xex_errno = XERR_TRUNCATED; + } + } + + return c; +} + +int xex_fread_seg_header(xex_segment *seg, FILE *file) { + int c, d; + unsigned short addr; + + xex_errno = XERR_NONE; + + seg->has_ff_header = 0; + seg->object = NULL; + + c = read_char(file); + if(c == EOF) { + if(feof(file)) + xex_errno = XERR_NONE; + + return 0; + } + d = read_char(file); + if(d == EOF) return 0; + + addr = XEX_ADDR(c, d); + if(addr == 0xffff) { + seg->has_ff_header = 1; + c = read_char(file); + if(c == EOF) return 0; + d = read_char(file); + if(d == EOF) return 0; + addr = XEX_ADDR(c, d); + } + + if(addr == 0xffff) { + xex_errno = XERR_REVERSED; + return 0; + } + + seg->start_addr = addr; + + c = read_char(file); + if(c == EOF) return 0; + d = read_char(file); + if(d == EOF) return 0; + addr = XEX_ADDR(c, d); + seg->end_addr = addr; + + seg->len = seg->end_addr - seg->start_addr + 1; + + if(seg->end_addr < seg->start_addr) { + xex_errno = XERR_REVERSED; + return 0; + } + + if(xex_verbose) { + fprintf(stderr, "xex_fread_seg_header(): read header:\n"); + xex_print_seg_info(seg); + } + + return 1; +} + +int xex_fread_seg_data(xex_segment *seg, FILE *file) { + int res; + + xex_errno = XERR_NONE; + + res = fread(seg->object, 1, seg->len, file); + xex_sys_errno = errno; + + if(xex_verbose) { + fprintf(stderr, "xex_fread_seg_data(): read data:\n"); + xex_print_seg_info(seg); + } + + if(res == seg->len) + return 1; + + if(ferror(file)) { + xex_errno = XERR_SYSCALL; + } else { + /* EOF or short read */ + xex_errno = XERR_TRUNCATED; + } + + return xex_check_seg(seg); +} + +int xex_fread_seg(xex_segment *seg, FILE *file) { + unsigned char *tmp = seg->object; + int res; + + xex_errno = XERR_NONE; + + if(tmp == NULL) { + if(xex_verbose) + fprintf(stderr, "xex_fread_seg(): seg->object == NULL\n"); + xex_errno = XERR_NULLDATA; + return 0; + } + + res = xex_fread_seg_header(seg, file); + seg->object = tmp; + + if(!res) + return 0; + + if(!xex_fread_seg_data(seg, file)) + return 0; + + return 1; +} + +int xex_fwrite_seg(xex_segment *seg, FILE *file) { + int res; + + xex_errno = XERR_NONE; + + if(xex_verbose) { + fprintf(stderr, "xex_fwrite_seg(): about to write:\n"); + xex_print_seg_info(seg); + } + + if(!xex_check_seg(seg)) + return 0; + + if(seg->has_ff_header) { + putc(0xff, file); + putc(0xff, file); + } + + putc(XEX_LSB(seg->start_addr), file); + putc(XEX_MSB(seg->start_addr), file); + putc(XEX_LSB(seg->end_addr), file); + putc(XEX_MSB(seg->end_addr), file); + + res = fwrite(seg->object, 1, seg->len, file); + if(res == seg->len) + return 1; + + if(ferror(file)) { + xex_errno = XERR_SYSCALL; + xex_sys_errno = errno; + } else { + xex_errno = XERR_TRUNCATED; + } + + return 0; +} + +int xex_get_object(xex_segment *seg, unsigned char *data) { + int offset = 0; + + if(!xex_check_seg(seg)) + return 0; + + if(seg->has_ff_header) { + data[0] = data[1] = 0xff; + offset = 2; + } + + data[offset] = XEX_LSB(seg->start_addr); + data[offset + 1] = XEX_MSB(seg->start_addr); + offset += 2; + data[offset] = XEX_LSB(seg->end_addr); + data[offset + 1] = XEX_MSB(seg->end_addr); + offset += 2; + + memcpy(&data[offset], seg->object, seg->len); + + return 1; +} + +int xex_check_seg(xex_segment *seg) { + int ret = 1; + + xex_errno = XERR_NONE; + + if(seg->end_addr < seg->start_addr) { + xex_errno = XERR_REVERSED; + ret = 0; + } + + if(seg->len != (seg->end_addr - seg->start_addr) + 1) { + xex_errno = XERR_TRUNCATED; + ret = 0; + } + + if(seg->object == NULL) { + xex_errno = XERR_NULLDATA; + ret = 0; + } + + if(xex_verbose && !ret && xex_errno != XERR_NULLDATA) + fprintf(stderr, "xex_check_seg() FAILED: %s\n", xex_strerror(xex_errno)); + + return ret; +} + +char *xex_strerror(int err) { + if(err < 0 || err >= XERR_MAXERR) + err = XERR_MAXERR; + + if(err == XERR_SYSCALL) + return strerror(xex_sys_errno); + else + return errors[err]; +} + +void xex_perror(char *msg) { + fprintf(stderr, "%s: %s\n", msg, xex_strerror(xex_errno)); +} + +void xex_print_seg_info(xex_segment *seg) { + xex_check_seg(seg); + fprintf(stderr, + "has_ff_header==%d start==%04X end==%04X " + "len==%04X status==%s\n", + seg->has_ff_header, seg->start_addr, seg->end_addr, + seg->len, xex_strerror(xex_errno)); +} |