1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
#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 run address from a segment, if there is one. This can handle the case
where the address is embedded in a larger segment (that doesn't just consist
of 2 bytes loaded at RUNAD). Returns -1 if there is no run address. */
int xex_get_run_addr(xex_segment *seg);
/* Same as above, for init address. */
int xex_get_init_addr(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 */
|