-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfileformatng.html
898 lines (873 loc) · 39.9 KB
/
fileformatng.html
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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
<html><head><title>8 Bit binary relocation format</title>
<link rev=made href="mailto:[email protected]">
</head>
<body bgcolor="#ffffff">
<h1>8 Bit binary relocation format - working draft</h1>
<h2>V2 as of 01 apr 2005</h2>
<h3>(c) André Fachat ([email protected])</h3>
<hr>
<h3>Foreword</h3>
<p>
This file format stems from the previously developed <code>o65</code> file format, which has proven to be valuable for a lot of purposes in the 6502 world.
However, there have been requests to include additional features into <code>o65</code> to support other CPUs and larger systems.
The beauty of <code>o65</code> is its simplicity, and implementing more features in a compatible way would lead to a more complicated format, which would loose this beauty.
For this reason, I have decided to define a new file format, <code>o65-v2</code> that incorporates more features for an extended set of requirements.
Nevertheless this format is based on <code>o65</code> (in its <a href="fileformat.html">version 1.3</a> and tries to keep the beauty of it - although in an
incompatible way.
</p><p>Here are the changes to the <code>o65</code> v1.3 version.
<ol>
<li>A New header word for <a href="#change20a">CPU type</a>, removing the old CPU2 bits from the mode header.</li>
<li>A New header word for <a href="#change20b">OS type</a>.</li>
<li>A New header word for <a href="#change20c">chain section identification</a>.
<li>A New header mode bit for <a href="#change20d">byte order</a>.
<li>New relocation table entry types for <a href="#change20e">32 bit addresses</a>.
</ol>
</p>
<hr>
<p>
<h3>1) Preface</h3>
<p>
With some new 8 bit operating systems comes the need for a new
binary format. In multitasking operating systems like Lunix, SMOS, or
OS/A65, a binary file cannot be loaded to a fixed location that might
already be used by another program. Therefore it must be possible to
relocate the program to an arbitrary address at least at load time.
In addition to that, more specific information might be stored in a
binary executable file, like interrupt vectors for example.
<p>
This text gives a good solution to this problem for the 6502 CPU and an
assembler source format to use this format in a general manner. The file
format can even be used as an object file format, i.e. a format a linker
can use as an input file.
<p>
<h3>2) binary format</h3>
<h4>2.1) General</h4>
<p>
The file differs from simple file formats, in that a lot
more information is stored in the file. First the data is structured
in separate segments to allow different handling of text (program code),
data (like tables) and bss (uninitialized data).
<p>
Also tables are included to allow late binding, i.e. linking the
file with other files at load time, and relocation, i.e. executing
the file at different addresses in the CPU's address space.
<p>
<h4>2.2) Segments</h4>
<p>
As already used in other formats, the assembler uses three different
segment types, i.e. text (the actual program code, read only and executable),
data (initialized variables, read write and executable),
<code>Ed. Note: for self modifying code?</code>
and bss (uninitialized variables, read write and not executable).
To have these different segments seems to be 'overdesigned', but they
actually make memory handling easier in more complex operating systems
or systems with virtual addresses (OS/A65, for example).
<p>
The text segment is defined to be read-only memory. This doesn't allow
self-modifying code in this segment, but allows memory sharing in virtual
memory architectures. The data segment actually is like the text segment,
only it is allocated writable. This segment might not be shared between
different processes. The contents of these two segments are loaded from
the file. The bss segment is uninitialized data, i.e. upon program start,
it is not defined - and not loaded from the file. This area is read-write
and can be used during program execution. It is also not shared between
processes. In addition to these segments, the file format also includes a
zeropage segment type, to allow zeropage variables to be relocated.
The "zeropage" is a segment that allows "short" addressing modes and its
meaning varies from CPU type to CPU type. In a 6502
for example this is the zeropage, in a 65816 this is the bank zero segment.
This zeropage segment is like a bss segment, in that only the length, but
not the data is saved.
<p>
The different segments hold different type of data and can be located
anywhere in memory (except zero segment, which has to be in the zeropage
resp. bank zero). The program must therefore not assume anything about
the relative addresses between different segments.
<p>
<h4>2.3) Relocation</h4>
<p>
In general, there are three ways to handle the relocation problem so far:
<ol>
<li>Tables: have a relocation table for a text segment. The table
can be located in front of the code, but then the relocation table must
be stored in a side-storage to be used when the code is being loaded.<br>
If the table is located behind the code, the code must be loaded first and
then relocated when the table is loaded.
<li>Deassembling: The loader goes through the code, "deassembles" it and
changes all absolute addresses. The Problem here is that the loaded needs
to know or have hints about where data is in the code.
<li>Relocation info in the code: here each address is preceeded with an
'escape' code and is relocated when loading. This approach requires that the
code is loaded bytewise to be relocated on the fly. So it disallows block
oriented transfer from storage media to memory.
</ol>
This binary format uses the first method, with the table after the
code/data. This way block oriented transfer for the text/data segment can
be used. And while reading the relocation tables bytewise, the relocation
can be done without the need to save the table somewhere.
<p>
<h4>2.4) External References & Exported Globals</h4>
<p>
As this file format should not only be used as an executable format, but
also as object file format, it must provide a way to define references
- references exported from this object and labels referenced in this
object. The external references list (also called 'undefined list') lists
the addresses where labels not defined in this object are referenced.
The exported globals list lists the addresses that are available for
other objects. The labels are named by null-terminated strings.
<p>
Even an executable file can have non-empty globals and externals lists,
but only if the operating system allows this. In this case, so called
'late binding' is used to link the object with some global libraries
at link time.
<p>
<h4>2.5) File extension</h4>
<p>
The proposed standard extension for the described format is ".o65" when
used as an object file.
<p>
<h4>2.6) Format description</h4>
<p>
The binary format is the following:
<pre>
(
header
text segment
data segment
external references list
relocation table for text segment
relocation table for data segment
exported globals list
)
</pre>
The description of the parts follows:
<p>
<h5>2.6.1) Header</h5>
<p>
The header contains the minimum needed data in a fixed struct.
The rest of the necessary information is put into the header options.
[Note: .word is a 16 bit value, .byt is a simple byte.
.long is a 32 bit value. .size is a 16 or 32 bit
value according to .word and .long, depending on the size bit in the
mode field. Byte order for all fields according to the order bit in the
mode field. ]
<p>
This is the fixed struct:
<pre>
(
.byt $01,$00 ; non-C64 marker
.byt $6f, $36, $35 ; "o65" MAGIC number!
.byt 1 ; version
.byt mode0, mode1 ; mode word
.byt cpu0, cpu1 ; CPU type
.byt os0, os1 ; operating system id
.byt sect0, sect1 ; operating system specific section marker
.size tbase ; address to which text is assembled to
; originally
.size tlen ; length of text segment
.size dbase ; originating address for data segment
.size dlen ; length of data segment
.size bbase ; originating address for bss segment
.size blen ; length of bss segment
.size zbase ; originating address for zero segment
.size zlen ; length of zero segment
.size stack ; minimum needed stack size, 0= not known.
; the OS should add reasonable values for
; interrupt handling before allocating
; stack space
)
</pre>
The mode bytes currently have these defined bits:
<a name="change13b"></a>
<pre>
mode1.7 : order 0= LSB first 1= MSB first byte order
mode1.6 : reloc 0= bytewise... 1= page(256byte)wise relocation
allowed
mode1.5 : size 0= size=16 bit, 1= size=32 bit
mode1.4 : obj 0= executable 1= object file
mode1.3 : simple 0= (ignored) 1= simple file addresses
mode1.2 : chain 0= (ignored) 1= another file follows this one
mode1.1 : bsszero 0= (ignored) 1= the bss segment must be zeroed out for this file
mode0.0-1: align 0= byte align,
1= word (i.e. 2 byte) align
2= long (4 byte) align
3= block (256 byte) align
</pre>
Here is a description of the mode bits:
<ul>
<li><a name="change20d">The <code>order</code> bit tells the loader the byte order of the fields
in the header that depend on the size bit. </a>
Zero means low byte first, one means high byte first.
If size is 32, then zero means the order <code>2^0, 2^8, 2^16, 2^24</code>,
while one means the order <code>2^24, 2^16, 2^8, 2^0</code>.
<li>The <code>reloc</code> bit defines if an object file can be relocated bytewise,
or if it must be page-aligned. A page has 256 bytes. The restriction to
pagewise relocation simplifies the relocation table and also allows
simpler compilers/assemblers.
<li>The <code>size</code> bit determines the size of the segment base address and length
entries. Currently the 16 bit size (size bit = 0) works for 6502 and
65816 CPUs.
<li>
The <code>obj</code> bit distinguishes between object files and executables.
An object file is used as assembler output that can be linked with
other object files to build an executable or an object library.
<li>The <code>simple</code> bit signals the loader that the load addresses have a specific form.
This form fulfills the following conditions:
<pre>
dbase = tbase + tlen
bbase = dbase + dlen
</pre>
This condition ensures that the loader can actually load the text and data segments in
one block, and can then use the same base address for the relocation of all three, the
text, data and bss segments. The <code>simple</code> mode bit is optional, in that when it is set the conditions
must be fulfilled, but if not set the conditions may or may not be fulfilled.
<li>The <code>chain</code> bit signals the loader that after the current o65 "file"
there is another "file" appended to the actual file on disk. This way "multi-o65" files can be
built. An "o65" file in a multi-o65 file is here now called "section".
Chaining allows the following scenarios:
<ol>
<li>Init code in a separate segment - the chain contains a first o65 section with the code to run
the program, and a second o65 with initialization code that can be thrown away after init.
As the init code may just as any program need zero-, data- and bss segments, a full o65
file structure is provided in the section.
<li>
Larger systems have mapped memory. The chain bit allows to
provide different sections to be loaded in different memory mappings in a single file.
<li>Fat binaries: A single file could hold different o65 sections, one for each
different type of CPU. The loader could ignore the parts that do not fit the CPU
that it is running on.
</ol>
The loader may support binding undefined
references in a later section to global labels exported from an earlier section.
Otherwise the operating system should provide calls to access the separate
sections, e.g. when they are loaded into different memory mappings.
The next o65 section starts again with the header (including non-C64 marker and magic number),
so sections with different characteristics may be chained.
The last section must have <code>chain=0</code>.
The <code>chain</code> bit is optional, if it is set and a loader does not support it, the file
may be rejected right away.
For these purposes the loader should have a means of identifying
different sections and their purposes. This can be done using the
<code>sect</code> header field.
<li>The <code>bsszero</code> bit tells the loader that the executable to be loaded requires the bss segment to be zeroed out. If it is not set, then the code must not assume any special value in the bss segment (which is the default behaviour for o65 version 1.2 and below). A loader that does not support zeroing out the bss segment must reject a file with this bit set.
<li>The two <code>align</code> bits give the address boundary the segments can be
placed. Even the 6502 needs this, as, for example, "jmp ($xxFF)" is
broken. The align bits are valid for all of the segments.
[Note: if reloc=1, then align should be 3. But if align=3, reloc need
not be 1, because reloc switches to a simpler version of the relocation
table. The reloc bit might be obsoleted in newer versions of this
format. Though it should be set, if necessary.]
</ul><p>
All unused bits in the mode field must be zero. A loader should reject files with bits set that it does not understand.
</p><p>
<a name="change20a">The <code>cpu</code> field determines the type of CPU. It consists of two
bytes that determine CPU family and version respectively.</a> See the appendix
for the definition of currently known CPUs.
</p><p>
<a name="change20b">The <code>os</code> field determines the type of Operating System.</a>
It consists of two
bytes that determine OS family and version respectively. See the appendix
for the definition of currently known OSes.
</p><p>
<a name="change20c">The <code>sect</code> field determines the type of section (in a chained
file).</a> It is operating system specific and consists of two
bytes that could for example determine which sections must be loaded into
the same memory mapping or other specific requirements for that specific
section. Must be zero if not used.
</p><p>
Note that the header size is 32 if the size bit is zero and 50 if the
size bit is one.
<p>
The fixed sized struct is immediately followed by a list of header options.
Each header option consists of a single byte total length, a type byte
and some data bytes if needed. A single length byte of $00 ends the
header option list.
<pre>
(
{ ; optional options, more than one allowed
.byt olen ; overall length (including length and type
; byte
.byt otype ; option type
[ .byt option_bytes ]
}
.byt $00 ; end of options marker (i.e. option len=0)
)
</pre>
The header options currently defined/proposed are:
<a name="change13a"></a>
<pre>
- Filename:
type=0; len=strlen(filename_in_ascii)+3; content="filename_in_ascii",0
The string contains the name of the object.
- Operating System Header
type=1; len=?
the first data byte is the OS type:
1 OSA/65 header supplement
2 Lunix header supplement
3 CC65 generic module (new in v1.3)
4 opencbm floppy modules (new in v1.3)
[others to follow?]
the following data contains OS specific information.
A suggested data byte is the OS version as second byte.
- Assemblerprogram:
type=2; len=strlen(ass)+3; content="ass",0
The string contains the name of the assembler resp. linker that produced
this file/object.
For example (syntax see below)
.fopt 2, "xa 2.1.1g",0
becomes
0c 02 78 61 20 32 2e 31 2e 31 67 00
in the file.
- Author:
type=3; len=strlen(author)+3; content="author",0
The string contains the author of the file.
- Creation data:
type=4; len=strlen(date)+3; content="date_string",0
The string contains the creation date in format like:
"Sat Dec 21 14:00:23 MET 1996", where we have the day, Month, date,
time, timezone and year. See output of `date`...
</pre>
<h5>2.6.2) text and data segments</h5>
<p>
The text and data segments are just the assembled code.
The only difference between text and data segments is the read/write mode
of the two segments. Therefore, to be compliant to this file format,
self-modifying code goes into the data segment.
<p>
<h5>2.6.3) Undefined references list</h5>
<p>
The next list is an ASCII list of labels that are referenced in this file
but not defined. The lists is preceeded with the number of undefined labels
(16 or 32 bits, according to the mode.size bit).
<pre>
undef_list: number_of_undefined_labels.s
"undefined_label1",0
"undefined_label2",0
...
</pre>
<p><a name="change13g"></a>
The character encoding and length of the names of the undefined labels should be
appropriate for the target platform, that may define additional constraints.
The encoding must allow zero-terminated byte arrays
as string representations. To allow short loading times, the names should not be
exceedingly long.
</p>
<h5>2.6.4) Relocation tables</h5>
<p>
The relocation tables are the same format for the two segments, text and
data. In general a relocation entry consists of the offset from the
previous relocation address to the next one, the type of the relocation
and additional info. Relocation not only defines the relocation when
moving object code to a different address, but also filling in the
undefined references.
<p>
Each table starts at relocation address = segment base address -1.
I.e. if the segment base address is $1000 for example, the first entry
has an offset computed from base address-1 = $0fff.
The offset to the next relocation address is the first byte of each
entry. If the offset is larger than 254 (i.e. 255 or above), than a
255 is set as offset byte, the offset is decremented by 254 (note the
difference) and the entry is started again.
<pre>
{ [255,...,255,] offset of next relocation (b), typebyte|segmentID [, low_byte] }+
</pre>
<a name="change20e">where typebyte has the bits 4, 5, 6 and 7 and is one of</a>
<pre>
WORD $80 2 byte address (or low word of a 4 byte address)
HIGH $40 high byte of a 2 byte address
LOW $20 low byte of a 2 byte address
SEGADR $c0 3 byte address (65816)
SEG $a0 segment byte of 3 byte address
LONG $60 4 byte address
HWORD $10 high word of a 4 byte address
</pre>
The segmentID stands for the segment the reference points to:
<pre>
0 undefined
1 absolute value
2 text segment
3 data segment
4 bss segment
5 zero segment
</pre>
(Of course the absolute value will never appear in a relocation table,
but this value is necessary for the exported list)
</p><p>
Please note that although the byte order may be MSB or LSB, the byte order
in the relocation table is fixed as described here. As the relocation
table will most probably be read bytewise, this should not matter. In the
code, however, the addresses will be handled according to the header
mode bit <code>order</code>.
</p><p>
If the type is HIGH, the low byte of the value is stored behind the
relocation table entry, if bytewise relocation is allowed (header mode
field bit 14). If only pagewise relocation is allowed
and the low byte is implicitely set zero
(i.e. it is _not_ saved in the relocation table).
</p><p>
If the type is HWORD, then the lower word of the address is stored in the
relocation table, lower byte first.
</p><p>
If the type is SEG, then the two lower bytes of the three byte segment
address are stored behind the entry in the relocation table, lower byte
first.
<p>
<!---
If the segment is "undefined", then the whole relocation entry is followed
by a two (mode.size=0) or four (mode.size=1) byte value index in the
undefined references list.
--->
<a name="change12a"></a>
If the segment is "undefined", the typebyte is immediately followed
by the two (mode size=0) or four (mode size=1) byte value index
in the undefined references list. If it is a high byte relocation,
the low byte is saved behind the index value (Note: the relocation
value need not be automatically zero, as it can be an offset to
the value of the undefined reference). The index value
determines the undefined reference, which must be looked up by the
loader. When the value of the undefined reference is found it is added
to the the value of the relocation address in the segment.
<p>
The value taken from the relocation address in the segment, together with
the low byte/lower bytes from the relocation table (if HIGH/SEG/HWORD entry)
form the address (or the lower parts of it in case of WORD for 4 byte address)
used if the segment would be used unrelocated. To relocate the segment,
the difference between the relocated segment base address and the segment
base address from the file is then added to the above address. The result
is again saved in the segment.
<p>
A zero offset byte ends the relocation table. The first offset is computed
from the segment base address-1, to avoid a 0 value in the first entry.
<p>
[Ed. Note: needs to go into appendix to CPU codes/specifics]
Note that direct addressing modes do not generate entries in the
relocation table. instead it is assumed that the 65816 direct register
holds the correct value (i.e. zero segment base address) when running
this program.
<p>
Example (for file contents see appendix B.1):
<p>
Segment Base address in file (header.tbase) is $1000.
The start address of the text segment after relocation is real.tbase = $1234.
<p>
Now the first (unrelocated) address at which a relocation should take
place is here:
<pre>
$1222 A9 23 lda #>vector
</pre>
<!--
This generates the offset: $1222-($1000-1) = $223. This is larger than
254 ($fe), so the first byte is 255 ($ff). The offset is decremented
by $fe, and gives $125. This again is larger than $fe, so the next byte
is $ff again. After substracting $fe again, we have $27. But this is
the address of the opcode. To get the address of the address byte, we
have to add 1 to get $28, which becomes the third byte.
--><a name="change13e"></a>
To compute the relocation table entry, we have to identify the address
that must be relocated. This is not the opcode address $1222, but the
address of the parameter to the offset, i.e. $1223. The first relocation
table entry offset is calculated from the start of the segment minus one, i.e.
$0fff in this case. The offset to be stored in the relocation table
therefore is $1223-$0fff=$224. This is larger than $fe, therefore the
first byte in the relocation table entry is $ff, and the offset is
decremented by $fe, which results in $126. This again is larger than
$fe, so the next byte in the relocation table entry is $ff again and the
offset is decremented by $fe, resulting in $28. This offset becomes the
next byte in the relocation table entry.
The offset for the next relocation table entry is then computed from $1223,
because this is the last relocation address.
<p>
Now we reference the high byte of an address, lets say vector=$23d0 (not
relocated), in the text segment. Therefore the relocation type becomes
'HIGH | text_segmentID = $42', which is the next byte. Because we are
referencing a high byte of an address, the low byte of the unrelocated
address is saved behind the typebyte in the relocation entry. This byte
is missing when referencing a low byte or address.
<p>
The relocation table entry is now:
<pre>
$ff, $ff, $28, $42, $d0.
</pre>
When actually doing the relocation, the relocation pointer is initialized
to real.tbase-1 = $1233 (this value correlates to the unrelocated text segment
start minus one, $0fff). Then we add the offset of $224 from the first
relocation table entry, which brings
us to $1457, where the parameter byte of the opcode is after loading
the file to $1234. We now have to compute the new address, where <code>vector</code>
is after relocation. So we take the unrelocated low byte from the
relocation table ($d0) and the high byte from $1457 ($23).
<pre>
vector_file = ($23 << 8) + $d0 = $23d0
</pre>
To this value we add
the difference between the address the program is assembled to and the
real load address:
<pre>
vector_relocated = vector_file + (real.tbase - header.tbase)
= $23d0 + ($1234 - $1000)
= $23d0 + $234
= $2604
</pre>
From this value the high byte is then written back to the address $1457.
Had we not saved the low byte in the relocation table, and only added
the high bytes, we would have missed the carry bit that increments
the high byte in this case!
<p>
Had "vector" now been an undefined reference, and "vector" would be
the second label in the undefined references list, we would get the
following relocation table entry (assuming mode.size=0):
<pre>
$ff, $ff, $28, $40, $02, $00, $00
</pre>
The value computed with the above formula for vector_file is now added
to the address the label "vector" now really has (This must of course
be looked up into an external table or list).
Had the opcode been "LDA #>vector+$567", then the low byte in the relocation
table would be $67, while the high byte in the opcode would be $05.
This value would result in vector_file and the real address of "vector"
would be added before wrting back the high byte to the opcode.
<p>
<h5>2.6.5) exported globals list</h5>
<p>
The global list is a list of names, together with the target segment
and the offset in the segment for each name. It is preceeded with the
number of exported labels. This allows the loader to allocate a table
large enough, if needed. The number of labels and the offset value
are 16 bit or 32 bit values according to the size bit in the header mode
field. The segmentID is a byte value and the same as in the relocation
table entry (see section 2.6.3).
<pre>
number_of_exported_labels.s
"global_label_name_in_asc1",0, segmentID.b, value.s
...
</pre>
<a name="change13c"></a>
<p>Note: an undefined reference can not be exported. Doing this would lead
to circular references for example when linking multiple object files,
therefor it is not allowed.
</p><p><a name="change13f"></a>
The character encoding and length of the names of the undefined labels should be
appropriate for the target platform, that may define additional constraints.
The encoding must allow zero-terminated byte arrays
as string representations. To allow short loading times, the names should not be
exceedingly long.
</p>
<h3>3) assembler source format</h3>
<p>
The assembler source format is a suggestion only. It will be implemented
in xa65, a cross assembler for 6502 CPUs running on Unix/Atari ST/Amiga
as a reference platform.
<p>
The assembler provides a way to embed absolute address code in relocatable
code. This is needed when code should be copied to a specific location
known at assemble time.
There also is a way to make a file 'romable'. You can give the start
address of the _file_ in ROM, and the assembler automatically sets
the text segment start address to where the code will be in the ROM.
Of course, the other segments must be taken care of with -b? command
line parameter, that set the segment start address.
<p>
<h4>3.1) embed absolute code in relocatable files</h4>
<p>
When the assembler is started in relocatable mode, everything is put into
a .o65 relocatable file. All address references generate relocation table
entries. If a "*= value" pseudo opcode is encountered,
then the assembler switches to absolute mode. The following opcodes don't
generate relocation table entries. If a "*=" without a value is read,
then the assembler switches back to relocatable mode. The relocation
program counter is increased with the length of the absolute part and
the absolute code is embedded between the relocatable parts.
<p>
<h4>3.2) embed relocatable code in absolute files</h4>
<p>
This is dropped - too complicated. Should better be done with some
objdump or linker programs or so.
<p>
<h4>3.2) Header options</h4>
<p>
Before any opcode (after starting in relocatable mode, or after a .reloc
opcode), a header option can be set by:
<pre>
.fopt byte1, byte2, ...
</pre>
The header option length is automatically set by the assembler.
An example for an file author entry:
<pre>
.fopt 3, "Andre Fachat",0
</pre>
The 3 is the type byte for the author header option. The last zero ends
the name. The assembler can be configured to automatically include an
assembler header option into a file header.
<p>
<h4>3.3) allocation of data segment/zeropage segment address space</h4>
<p>
The assembler switches between the different segments by the means of
".text", ".data", ".bss" and ".zero" pseudo opcodes. After starting in
relocatable mode, the assembler is in the text segment.
<p>
The text segment contains the program code. Data holds the initialized data,
while bss and zero segments contain uninitialized data for normal/zeropage
address space.
Everything that is between one of these segment opcodes and the next segment
opcode gets into the corresponding segment, i.e. labels, assembled code etc.
The text and data segments are saved in the file, while for the bss and
zero segments only the length is saved in the file.
<p>
The assembler should issue a warning when a direct addressing mode
is used without a zero segment address and vice versa for 65816 CPUs.
<p>
<h4>3.4) referencing data/bss/zeropage addresses</h4>
<p>
One problem with the 6502 is, that it cannot load an address within one
step or assembler opcode. So an address is loaded with standard byte
opcodes, like "lda #<label". But how do we decide, whether "label"
is an address or not, and what do we if we get something like
"lda #zp_label + 12 * label2"?
<p>
The assembler is now intelligent enough to evaluate such expressions
and check for:
<pre>
- no address label : ok, absolute
- one address label, only add to label : ok, relocate
- difference between two addresses : If addresses in same segment, compute
diff and set absolute, otherwise bail
- everything else : warning
</pre>
This way there is no change in syntax. Address labels are distinguished
by using the "label:" syntax, as opposed to "label = value".
Also, if the assembler is capable of doing so, an address label may be
defined by "label opcode", i.e. without a colon.
<p>
<h4>3.5) aligning code</h4>
<p>
The 6502 has the problem that some opcodes (e.g. "JMP ($xxFF)" are
broken, if the address given is at some (odd) address. But when loading
a relocatable file, one cannot know if an address will be odd or even.
Therefore there is a new opcode,
<pre>
.align 2
</pre>
that aligns the next address at the given address boundary. Valid
values are 2, 4, and 256. For a 6502 CPU it may insert NOP opcodes
($EA) until the alignment is ensured. Also the header alignment bits
are set appropriately.
<p>
<h3>4) Additional Notes</h3>
<h4>4.1 Clearance</h4>
<p>
This file is surely not the optimum and could be improved. Also the
header option "assigned numbers" should be added here.
<p>
For this reason the author, André Fachat, will function as a
clearing point, where problems can be discussed and numbers can be assigned.
<h4>4.2 Character Sets</h4>
<h2>Appendix</h2>
<h3>A) CPU codes & specifics</h3>
This section describes the different CPUs known, assigns codes and also
how certain CPU specific features are handled.
The known CPUs are summarized in this table:
<table border="1">
<tr><th>CPU family</th><th>cpu0</th><th>CPU types</th><th>cpu1</th><th>Description</th></tr>
<tr><td>6502</td><td>$65</td><td>6502 core</td><td>$00</td><td>Core 6502 opcodes used, i.e. no undocumented opcodes</td></tr>
<tr><td></td><td></td><td>65C02</td><td>$01</td><td>CMOS 6502, no undocumented opcodes, bug fixes</td></tr>
<tr><td></td><td></td><td>65SC02</td><td>$02</td><td>enhanced CMOS 6502, some new opcodes</td></tr>
<tr><td></td><td></td><td>65CE02</td><td>$03</td><td>some 16 bit ops/branches, Z register</td></tr>
<tr><td></td><td></td><td>N6502</td><td>$04</td><td>6502 core plus NMOS undocument opcodes</td></tr>
<tr><td></td><td></td><td>65816emu</td><td>$05</td><td>6502 emulation mode of the 65816 CPU</td></tr>
<tr><td>65816</td><td>$66</td><td>65816 native mode</td><td>$00</td><td>Native 65816</td></tr>
<tr><td>6809</td><td>$69</td><td>6809 native mode</td><td>$00</td><td>Native 6809</td></tr>
<tr><td>Z80</td><td>$28</td><td>Z80 core</td><td>$00</td><td>Original Z80 code</td></tr>
<tr><td>8080</td><td>$80</td><td>8080</td><td>$00</td><td></td></tr>
<tr><td>8086</td><td>$86</td><td>8086</td><td>$00</td><td></td></tr>
<tr><td></td><td></td><td>8088</td><td>$88</td><td></td></tr>
<tr><td>80286</td><td>$82</td><td>80286</td><td>$00</td><td></td></tr>
</table>
<p>
<h4>6502 CPU family</h4>
The 6502 is a widely used 8 bit CPU with plain 64k address space.
In this file format, the <code>zero</code> segment is used to allocate
address space in the zeropage area $00xx.
The 6502 has the special feature of a 'zeropage', i.e. a very limited
memory address range used for special addressing modes. These addressing mode allow shorter opcodes
and thus shorter execution time when loading data from the address range $00xx.
So the format
should not only provide a means to relocate absolute addresses but also
zeropage addresses.
<p>
The stack space is also very limited. A binary format has to provide a
measure of how much stack space is needed for the application.
<p>
Such limits should be defined as 2 byte values, even if the 6502 only has
a range of 8 address bits for zeropage and stack.
<p>
Another problem is, that an address can be 'split', i.e. you can just use
the high byte or the low byte separately in an opcode. This gives need
to a special relocation table format, that can cope with half-address
references.
<h4>65816 CPU family</h4>
The 65816 is an extension to the 6502. But it behaves
differently, it has a 16 bit stack pointer for example. For further
expandability, a 32 bit format should be provided, although the 16 bit
format suffices for the 65816 already.
The 65816 has banked memory and can address up to
sixteen megabyte of memory, using an 8 bit segment register for the upper 8 address bits.
Therefore the 65816 can even have three byte addresses, i.e. address
in a segment and segment number.
It still features a "direct" adressing mode similar to the 6502 zeropage.
The <code>zero</code> segment is used to allocate "bank zero" memory.
<p>
<h3>B) OS codes</h3>
The following operating systems are currently known:
<table border="1"><tr><th>OS</th><th>os0</th></tr>
<tr><td>OS/A65</td><td>1</td></tr>
<tr><td>Lunix</td><td>2</td></tr>
<tr><td>CC65 generic</td><td>3</td></tr>
<tr><td>opencbm</td><td>4</td></tr>
</table>
The header <code>os1</code> byte contains an OS-specific version number.
<h3>A) Late binding</h3>
<p>
<a name="change13d"></a>
Late binding means that during the assembler run the values of some
variables are not known. Instead these variable values are filled in
when the program file is loaded into the system.
</p><p>
As an example let's discuss an example for a program that needs to
access some hardware at the expansion port of the C64. The hardware
is located either at IO1 ($de00) or IO2 ($df00) depending on some
hardware switch. To allow to use only one executable for the program,
it uses a variable "IOPORT" that is not defined in the program itself,
but set by the o65 loader using late binding.
</p>
<p>
The program accesses the io port is using the variable:
<pre>
lda IOPORT
</pre>
When assembling this one line program, the assembler is told to accept the
variable IOPORT as undefined. In <code>xa</code> this is done
using the <code>-L</code> option:
<pre>
xa -R -LIOPORT -o program.o65 program.a65
</pre>
Then <code>program.o65</code> contains relocatable code with an
undefined reference named <code>IOPORT</code>. Every time the
code uses this variable, the relocation table contains an entry
with a reference to the label in the undefined reference table.
</p>
<p>The resulting file looks like:
<code>Ed. Note: needs to be redone</code>
<pre>
00000000 01 00 6f 36 35 00 00 00 00 10 03 00 00 04 00 00 |..o65...........|
00000010 00 40 00 00 04 00 00 00 00 00 00 ad 00 00 01 00 |.@..............|
00000020 49 4f 50 4f 52 54 00 02 80 00 00 00 00 00 00 |IOPORT.........|
</pre>
The first six bytes are the magic number. After that the mode bits
(bytes seven and eight) are all zero. The text segment starts at $1000
and has a length of 3. The data segment starts at $0400 with a length of zero,
and the bss segment starts at $4000 with a length of zero too.
The zerospace segment starts at $0004, but also with a length of zero.
The minimum stack size needed is zero too. The list of header options
starts at file offset $001a. As the first byte is zero, there is no header
option. After this follows the text segment containing the bytes
$ad $00 $00. If the data segment size would not be zero, the data segment
would come here.
Then the undefined references list follows. The first two bytes (at file offset
$001e) state that there is a single undefined reference, and the name of the
undefined reference "IOPORT" followed by the ending zero byte is stored after
this number. Then the relocation table follows. The first byte in the relocation
table is $02. As the relocation table offset starts at <code>tbase-1</code> this
means that the first relocation position is at the second byte (offset 1) in the
text segment. The type byte $80 defines that it is an undefined, absolute
reference. The next two bytes define the index in the undefined reference table,
in this case $0000, which means that the reference <code>IOPORT</code> is
referenced. The next byte is zero, signalling the end of the text segment
relocation table. At file offset $002b the relocation table for the data
segment starts. As the first byte is zero, there is no relocation entry
for the data segment (obviously, as the data segment is empty). After this
relocation table the number of exported globals follows. This is zero, as
there is no exported global variable.</p>
<p>
When loading the file, the loader must know in advance what value
IOPORT should be assigned. This is not further discussed here.
When the loader loads the file, and recognizes the name
<code>IOPORT</code> in the undefined references table, it remembers
the index of this name in the table. Then, when the relocation table
contains a reference to the undefined label with the index value for
<code>IOPORT</code>, the value for that variable is then used
in the relocation.
</p>
<p>
If, for example the loader knows that <code>IOPORT=$de00</code>, then the
text segment is relocated to
<pre>
$ad $00 $de
</pre>
If the source is changed for example to
<pre>
lda IOPORT+1
</pre>
one byte in the the file changes:
<code>Ed. Note: needs to be redone</code>
<pre>
00000010 00 40 00 00 04 00 00 00 00 00 00 ad 01 00 01 00 |.@..............|
^^
</pre>
If the file is then relocated, the loader adds the value in the opcode in the
text segment ($0001 in this case) to the value resuling from the reference resolution
(<code>IOPORT</code> in this case). The resulting code then becomes:
<pre>
$ad $01 $de
</pre>
</p>
<h3>B) File examples</h3>
<h4>B.1 Example from section 2.6.4</h4>
<p>The example source file is
<pre>
.text
.dsb $222,$aa
lda #>vector
.dsb $23d0-$1224,$55
vector = *;
</pre>
Using the command
<pre>
xa -R -o test2.o65 test2.a65
</pre>
results in this file
<code>Ed. Note: needs to be redone</code>
<pre>
00000000 01 00 6f 36 35 00 00 00 00 10 d0 13 00 04 00 00 |..o65...........|
00000010 00 40 00 00 04 00 00 00 00 00 00 aa aa aa aa aa |.@..............|
00000020 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa |................|
*
00000230 aa aa aa aa aa aa aa aa aa aa aa aa aa a9 23 55 |..............#U|
00000240 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 |UUUUUUUUUUUUUUUU|
*
000013e0 55 55 55 55 55 55 55 55 55 55 55 00 00 ff ff 28 |UUUUUUUUUUU....(|
000013f0 42 d0 00 00 01 00 76 65 63 74 6f 72 00 82 d0 23 |B.....vector...#|
00001400
</pre>
After relocating the file with
<pre>
ld65 -bt 4660 test2.o65
</pre>
to the new address $1234 (using <code>ld65</code> from the <code>xa</code>
package), the resulting file is:
<code>Ed. Note: needs to be redone</code>
<pre>
00000000 01 00 6f 36 35 00 00 00 34 12 d0 13 00 10 00 00 |..o65...4.......|
00000010 00 40 00 00 02 00 00 00 00 00 00 aa aa aa aa aa |.@..............|
00000020 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa |................|
*
00000230 aa aa aa aa aa aa aa aa aa aa aa aa aa a9 26 55 |..............&U|
00000240 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 |UUUUUUUUUUUUUUUU|
*
000013e0 55 55 55 55 55 55 55 55 55 55 55 00 00 ff ff 28 |UUUUUUUUUUU....(|
000013f0 42 04 00 00 01 00 76 65 63 74 6f 72 00 82 d0 23 |B.....vector...#|
</pre>
which confirms the addresses computed above.
<h4>B.x more examples</h4>
<p>
(to be done with reference assembler)
</body></html>