#ifndef XEX_H #define XEX_H /* A xex_segment represents one segment of a XEX file. */ typedef struct { unsigned short start_addr; unsigned short end_addr; unsigned char *object; int len; unsigned char has_ff_header; } xex_segment; /* object points to an array containing ONLY the data bytes, not the header bytes! Use xex_get_object() to reconstitute the full segment with headers. (Note that I'm using the word "object" in the sense of "object code", not in its OOP sense) */ #define XEX_RUNAD 0x2e0 #define XEX_INITAD 0x2e2 #define XEX_LSB(x) (x & 0xff) #define XEX_MSB(x) ((x >> 8) & 0xff) #define XEX_ADDR(x, y) ((unsigned char)x | ((unsigned char)y << 8)) /* All int functions return true for success or false for failure. On failure, the variable xex_errno will be set to one of the XERR_* constants. Caller may use xex_strerror(xex_errno) to get a human-readable error message (or xex_perror()). Note that XERR_NONE == 0, so you can use the construct: if(xex_errno) { // handle error here } */ #define XERR_NONE 0 #define XERR_SYSCALL 1 #define XERR_REVERSED 2 #define XERR_TRUNCATED 3 #define XERR_NULLDATA 4 #define XERR_MAXERR 5 extern int xex_errno; extern int xex_verbose; /* Initialize a xex_segment from the data pointed to by segment, which must not be null. segment must be a complete segment, including start/end addresses and possible $FFFF header. seg->object will point within the data, so don't free() it until you're done with seg! */ int xex_new_seg(xex_segment *seg, unsigned char *segment); /* Set up seg as an init address segment. object must have room for at least 2 characters. A copy of the object pointer is stored in seg->object, so don't free(object) until you're done with seg. */ int xex_init_seg(xex_segment *seg, unsigned char *object, unsigned short addr); /* Set up seg as an run address segment. object must have room for at least 2 characters. A copy of the object pointer is stored in seg->object, so don't free(object) until you're done with seg.*/ int xex_run_seg(xex_segment *seg, unsigned char *object, unsigned short addr); /* Since this library does NOT allocate any memory, reading a segment must be done in two pieces. xex_fread_seg_header() reads the 4- or 6-byte header and sets seg->len to the size of the segment. xex_fread_seg_data() then reads this number of bytes from the file, into seg->object. Example usage: xex_segment seg; xex_fread_seg_header(&seg, my_file); seg.object = malloc(seg->len); xex_fread_seg_data(&seg, my_file); // ...later, the caller must free(seg.object) when done with it. xex_fread_seg_header() returns 0 on EOF, with xex_errno set to XERR_NONE. It also returns 0 on failure, but xex_errno will be something other than XERR_NONE. */ int xex_fread_seg_header(xex_segment *seg, FILE *file); int xex_fread_seg_data(xex_segment *seg, FILE *file); /* If you're dealing with xex files sequentially, and want to use a static buffer instead of malloc(), you can use xex_fread_seg() instead. seg->object should point to a buffer large enough to hold any segment's object code (64K, to be on the safe side), or you WILL get segfaults. */ int xex_fread_seg(xex_segment *seg, FILE *file); /* xex_fwrite_seg() writes a segment to a file, which must be opened for output. */ int xex_fwrite_seg(xex_segment *seg, FILE *file); /* Extract the object code from a segment. data must have room for len+6 bytes (or len+4 if has_ff_header is false). data will be the raw segment data, suitable for writing to a XEX file. */ int xex_get_object(xex_segment *seg, unsigned char *data); /* Sanity check a segment. Make sure start_addr is less than end_addr, that it doesn't wrap around the 6502 address space, and so on. Returns true if segment is OK. */ int xex_check_seg(xex_segment *seg); /* Get human-readable error message. If xex_errno is XSYSCALL, the system's strerror() is called. */ char *xex_strerror(int err); /* Shortcut for xex_strerror(), like system's perror() */ void xex_perror(char *msg); /* Debugging aid */ void xex_print_seg_info(xex_segment *seg); #endif /* XEX_H */