aboutsummaryrefslogtreecommitdiff
path: root/xex.c
diff options
context:
space:
mode:
Diffstat (limited to 'xex.c')
-rw-r--r--xex.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/xex.c b/xex.c
new file mode 100644
index 0000000..e4b66ce
--- /dev/null
+++ b/xex.c
@@ -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));
+}