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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
.\" Man page generated from reStructuredText.
.
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "XEX" 5 "2024-07-09" "0.2.1" "Urchlay's Atari 8-bit Tools"
.SH NAME
xex \- Atari 8-bit executable file format.
.\" RST source for xex(5) man page. Convert with:
.
.\" rst2man.py xex.rst > xex.5
.
.SH DESCRIPTION
.sp
This manual describes the Atari 8\-bit binary load format, commonly known
as ".xex" or "binary load".
.sp
The file format consists of one or more segments, each having its own
load address. This is an absolute address in the Atari\(aqs memory map,
and there\(aqs no facility for relocating the code.
.SS File Structure
.sp
In pseudo\-EBNF, the file structure looks like:
.INDENT 0.0
.IP \(bu 2
ffff_header = "$FF", "$FF"
.IP \(bu 2
initial_segment = ffff_header, segment
.IP \(bu 2
segment = start_address, end_address, data
.IP \(bu 2
file = initial_segment, [ (initial_segment | segment) ... ]
.UNINDENT
.sp
In English:
.sp
Each segment has a 4\- or 6\-byte header. The first segment in the file
must use the 6\-byte header. Further segments can use either the 4\-byte
or 6\-byte header.
.sp
The 6\-byte header consists of:
.INDENT 0.0
.IP \(bu 2
2 bytes set to \fB$FF\fP\&.
.IP \(bu 2
The 2\-byte load address of the segment, in standard LSB\-first 6502 order.
.IP \(bu 2
The 2\-byte end address of the segment, in standard LSB\-first 6502 order.
This address must be greater than or equal to the load address.
.UNINDENT
.sp
The 4\-byte header is the same as the 6\-byte header, except it lacks the
initial \fB$FF\fP, \fB$FF\fP bytes.
.sp
The rest of each segment just consists of the data to be loaded,
exactly \fI(end_address \- load_address) + 1\fP bytes.
.SS Initialization
.sp
It\(aqs possible to run code in the middle of loading the file. Such code
is usually referred to as an an "init routine". A XEX file can have
multiple init routines, or none at all.
.sp
Init routines are normal segments. An init \fIaddress\fP is also a normal
segment: 2 bytes loaded at address \fB$02E2\fP, aka \fBINITAD\fP\&. As
soon as this segment is loaded, the DOS will jump (actually \fBJSR\fP)
to this address, which should exit back to DOS with an \fBRTS\fP
instruction. After the routine exits, DOS will continue loading the
file normally.
.SS Run Address
.sp
A .xex file can (and usually does) also have a run address. Unlike
init addresses, there can be only one run address. If the file
contains more than one run address, only the last one loaded has any
effect. The program loader jumps to the run address after the file
is done loading. It\(aqs possible for the program to exit via the 6502
\fBRTS\fP instruction, though many program (especially games) never
exit.
.sp
The run address segment is the one that loads the 2\-byte run address
into address \fB$02E0\fP, aka \fBRUNAD\fP\&.
.SS Raw Data Blocks
.sp
When init code runs, the file is still being loaded. It\(aqs open on IOCB
#1, and the init code can read some or all of the rest of the file
itself, before returning to DOS. The part of the file read by an init
segment doesn\(aqt have to conform to the regular XEX file structure, so
they\(aqre referred to here as raw data blocks.
.sp
Raw data blocks usually occur in files created with "packer" or
"compressor" programs, or occasionally in other large programs (Turbo
BASIC is an example). Raw data blocks are generally found just after
an init address segment. If you have an executable that loads just
fine on a real Atari or emulator, but fails with \fBxexcat\fP and
\fBxexamine\fP, a raw data block is usually the reason why.
.sp
Files with raw data blocks also can\(aqt be loaded by simple bootloaders
such as Fenders 3\-sector loader.
.SH EXAMPLES
.SS Assembly
.sp
Here is a simple assembly language program that changes the background
of the GRAPHICS 0 text screen to black, and the text to high\-intensity white:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
1000 *= $0600
1010 START
1020 LDA #0
1030 STA 710
1040 LDA #$0F
1050 STA 709
1060 RTS
2000 *= $02E0 ; AKA RUNAD
2010 .WORD START
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
To assemble this with either Atari\(aqs Assembler/Editor or OSS Mac/65,
the command is:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
ASM ,,#D:COLORS.XEX
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
This will create a binary file that looks like this:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
offset 0: FF FF 00 06 0A 06 A9 00
offset 8: 8D C6 02 A9 0F 8D C5 02
offset 16: 60 E0 02 E1 02 00 06
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
This is a complete (though short) XEX file, and it can be loaded from
the DOS menu (or D1: prompt, if you use a command\-line\-based DOS).
.sp
The first 2 bytes (\fB$FF\fP, \fB$FF\fP) are the signature for the initial
segment header.
.sp
The next 2 bytes (\fB$00\fP, \fB$06\fP) are the load address of the first segment
(\fB$0600\fP, in 6502\-style LSB\-first notation).
.sp
The next 2 bytes (\fB$0A\fP, \fB$06\fP) are the end address (\fB$060A\fP).
.sp
Since the header says to load data from \fB$0600\fP to \fB$060A\fP, there are 11
data bytes in the segment, beginning with \fB$A9\fP, \fB$00\fP (the 6502 object
code for the \fILDA #0\fP instruction), and extending to the \fB$60\fP (\fIRTS\fP
opcode) at offset 16.
.sp
The data from the first segment is immediately followed by the header
of the next segment, at offset 17. A \fB$FF\fP, \fB$FF\fP signature would be allowed
here, but in the example, the 2nd segment uses the 4\-byte header.
.sp
At offset 17, the \fB$E0\fP, \fB$02\fP (aka \fB$02E0\fP) is the load address. \fB$E1\fP, \fB$02\fP
(\fB$02E1\fP) is the end address. \fB$02E0\fP, \fB$02E1\fP is known as \fIRUNAD\fP in the
Atari world, and it\(aqs the address where DOS will find the entry point
to the program when it\(aqs done being loaded.
.sp
The next (and last) 2 bytes are \fB$00\fP, \fB$06\fP (aka \fB$0600\fP), which is the run
address itself (to be deposited at \fIRUNAD\fP).
.sp
There are no more segments, since we\(aqve reached end of file.
.SS Data Only
.sp
Since a XEX file can load arbitrary data at arbitrary addresses, there\(aqs
simpler way to accomplish the color changes. Instead of writing code to
do the job, we just create a XEX file that loads the colors we want
directly into the color shadow registers.
.sp
In BASIC, you\(aqd say:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
POKE 709,15
POKE 710,0
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
\&...but BASIC doesn\(aqt have an easy way to create a XEX file.
An assembler is a more convenient tool. The code is:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
1000 *= $02C5
1010 .BYTE $0F,$00
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
The binary looks like this:
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
ff ff C5 02 C6 02 0F 00
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
The file begins with \fB$FF\fP, \fB$FF\fP header, then the 2\-byte load address
(\fB$C5\fP, \fB$02\fP for address \fB$02C5\fP), then the 2\-byte end address (\fB$C6\fP, \fB$02\fP,
aka \fB$02C6\fP), then the 2 bytes of data to be stored there (\fB$0F\fP, \fB$00\fP).
.sp
When loaded, this will store \fB$0F\fP at address \fB$02C5\fP (aka \fICOLOR0\fP,
the text luminance in \fIGRAPHICS 0\fP) and \fB$00\fP at \fB$02C6\fP (\fICOLOR1\fP, the
\fIGRAPHICS 0\fP background color).
.sp
Notice that there\(aqs no run or init address. That\(aqs because no code is
executed; it\(aqs just 2 bytes of raw data.
.SH HISTORY
.sp
The first Atari 8\-bit binary load format was defined by Atari DOS 1.0,
in 1979. This was a multi\-segmented format, but not compatible with
the DOS 2.0S binary load format (aka the XEX format). Specifically,
files began with a \fB$84\fP, \fB$09\fP header (rather than the \fB$FF\fP,
\fB$FF\fP used by DOS 2.0S), and there was no such thing as an init
routine (DOS 1 didn\(aqt use or define \fBINITAD\fP). DOS 1.0 also used
\fBAUTO.SYS\fP rather than \fBAUTORUN.SYS\fP as the file to load at
startup. Very few (or probably \fIno\fP) files in this format still exist
today.
.sp
The format used by all other Atari and third\-party DOSes was
first defined by Atari DOS 2.0S. There was no standard filename
extension. Files were usually named .OBJ, .BIN, or .COM... except the
special filename \fBAUTORUN.SYS\fP, which was automatically loaded and
run by the DOS at startup.
.sp
Some later third\-party DOSes do use the filename extension \fB\&.COM\fP
for their own external commands. Typing \fIFOO\fP at the SpartaDOS or DOS
XL command prompt will load and run a file called \fBFOO.COM\fP, if it
exists. The external command files that ship with SpartaDOS also don\(aqt
have run or init addresses (so they won\(aqt run, if loaded by another
DOS). SpartaDOS apparently uses the load address (or maybe a fixed
address) as the entry point to \fB\&.COM\fP files.
.sp
The name "xex" and the filename extension ".xex" came into use on the
early Internet in the 1990s, to distinguish Atari executables from
e.g. MS\-DOS executables. XEX stands for "XE eXecutable", although
this is a double misnomer: there\(aqs nothing XE\-specific about the
file format (it applies to the 400/800 and XL machines, too), and
technically a binary load file isn\(aqt necessarily an executable; it
could just be a block of data without run or init addresses.
.SH COPYRIGHT
.sp
WTFPL. See \fI\%http://www.wtfpl.net/txt/copying/\fP for details.
.SH AUTHOR
.INDENT 0.0
.IP B. 3
Watson <\fI\%urchlay@slackware.uk\fP>; Urchlay on irc.libera.chat \fI##atari\fP\&.
.UNINDENT
.SH SEE ALSO
.sp
\fBa8cat\fP(1),
\fBa8eol\fP(1),
\fBa8xd\fP(1),
\fBatr2xfd\fP(1),
\fBatrsize\fP(1),
\fBaxe\fP(1),
\fBblob2c\fP(1),
\fBblob2xex\fP(1),
\fBcart2xex\fP(1),
\fBcxrefbas\fP(1),
\fBdasm2atasm\fP(1),
\fBdumpbas\fP(1),
\fBf2toxex\fP(1),
\fBfenders\fP(1),
\fBlistbas\fP(1),
\fBprotbas\fP(1),
\fBrenumbas\fP(1),
\fBrom2cart\fP(1),
\fBunmac65\fP(1),
\fBunprotbas\fP(1),
\fBvxrefbas\fP(1),
\fBwhichbas\fP(1),
\fBxex1to2\fP(1),
\fBxexamine\fP(1),
\fBxexcat\fP(1),
\fBxexsplit\fP(1),
\fBxfd2atr\fP(1),
\fBxex\fP(5),
\fBatascii\fP(7).
.sp
Any good Atari 8\-bit book: \fIDe Re Atari\fP, \fIThe Atari BASIC Reference
Manual\fP, the \fIOS Users\(aq Guide\fP, \fIMapping the Atari\fP, etc.
.\" Generated by docutils manpage writer.
.
|