]> git.sur5r.net Git - cc65/blob - src/co65/o65.c
Working
[cc65] / src / co65 / o65.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                   o65.h                                   */
4 /*                                                                           */
5 /*               Definitions and code for the o65 file format                */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2002-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39
40 /* common */
41 #include "chartype.h"
42 #include "xmalloc.h"
43
44 /* co65 */
45 #include "error.h"
46 #include "fileio.h"
47 #include "o65.h"
48
49
50
51 /*****************************************************************************/
52 /*                              struct O65Data                               */
53 /*****************************************************************************/
54
55
56
57 static O65Data* NewO65Data (void)
58 /* Create, initialize and return a new O65Data struct */
59 {
60     /* Allocate memory */
61     O65Data* D = xmalloc (sizeof (O65Data));
62
63     /* Initialize the fields as needed */
64     D->Options      = AUTO_COLLECTION_INITIALIZER;
65     D->Text         = 0;
66     D->Data         = 0;
67     D->TextReloc    = AUTO_COLLECTION_INITIALIZER;
68     D->DataReloc    = AUTO_COLLECTION_INITIALIZER;
69     D->Imports      = AUTO_COLLECTION_INITIALIZER;
70     D->Exports      = AUTO_COLLECTION_INITIALIZER;
71
72     /* Return the new struct */
73     return D;
74 }
75
76
77
78 /*****************************************************************************/
79 /*                                   Code                                    */
80 /*****************************************************************************/
81
82
83
84 static unsigned long ReadO65Size (FILE* F, const O65Header* H)
85 /* Read a size variable (16 or 32 bit, depending on the mode word in the
86  * header) from the o65 file.
87  */
88 {
89     switch (H->mode & O65_SIZE_MASK) {
90         case O65_SIZE_32BIT:    return Read32 (F);
91         case O65_SIZE_16BIT:    return Read16 (F);
92         default:                Internal ("Invalid size field value in o65 header");
93     }
94 }
95
96
97
98 static void ReadO65Header (FILE* F, O65Header* H)
99 /* Read an o65 header from the given file. The function will call Error if
100  * something is wrong.
101  */
102 {
103     static const char Magic[3] = {
104         O65_MAGIC_0, O65_MAGIC_1, O65_MAGIC_2   /* "o65" */
105     };
106
107     /* Read the marker and check it */
108     ReadData (F, H->marker, sizeof (H->marker));
109     if (H->marker[0] != O65_MARKER_0 || H->marker[1] != O65_MARKER_1) {
110         Error ("Not an o65 object file: Invalid marker %02X %02X",
111                H->marker[0], H->marker[1]);
112     }
113
114     /* Read the magic and check it */
115     ReadData (F, H->magic, sizeof (H->magic));
116     if (memcmp (H->magic, Magic, sizeof (H->magic)) != 0) {
117         Error ("Not an o65 object file: Invalid magic %02X %02X %02X",
118                H->magic[0], H->magic[1], H->magic[2]);
119     }
120
121     /* Read the version number and check it */
122     H->version = Read8 (F);
123     if (H->version != O65_VERSION) {
124         Error ("Invalid o65 version number: %02X", H->version);
125     }
126
127     /* Read the mode word */
128     H->mode = Read16 (F);
129
130     /* Read the remainder of the header */
131     H->tbase = ReadO65Size (F, H);
132     H->tlen  = ReadO65Size (F, H);
133     H->dbase = ReadO65Size (F, H);
134     H->dlen  = ReadO65Size (F, H);
135     H->bbase = ReadO65Size (F, H);
136     H->blen  = ReadO65Size (F, H);
137     H->zbase = ReadO65Size (F, H);
138     H->zlen  = ReadO65Size (F, H);
139     H->stack = ReadO65Size (F, H);
140 }
141
142
143
144 static O65Option* ReadO65Option (FILE* F)
145 /* Read the next O65 option from the given file. The option is stored into a
146  * dynamically allocated O65Option struct which is returned. On end of options,
147  * NULL is returned. On error, Error is called which terminates the program.
148  */
149 {
150     O65Option* O;
151
152     /* Read the length of the option and bail out on end of options */
153     unsigned char Len = Read8 (F);
154     if (Len == 0) {
155         return 0;
156     }
157     if (Len < 2) {
158         Error ("Found option with length < 2 (input file corrupt)");
159     }
160     Len -= 2;
161
162     /* Allocate a new O65Option structure of the needed size */
163     O = xmalloc (sizeof (*O) - sizeof (O->Data) + Len);
164
165     /* Assign the length and read the remaining option data */
166     O->Len  = Len;
167     O->Type = Read8 (F);
168     ReadData (F, O->Data, Len);
169
170     /* Return the new struct */
171     return O;
172 }
173
174
175
176 static O65Import* ReadO65Import (FILE* F)
177 /* Read an o65 import from the file */
178 {
179     O65Import* I;
180
181     /* Allow identifiers up to 511 bytes */
182     char Buf[512];
183
184     /* Read the identifier */
185     unsigned Len = 0;
186     char C;
187     do {
188         C = Read8 (F);
189         if (Len >= sizeof (Buf)) {
190             Error ("Imported identifier exceeds maximum size (%u)", sizeof (Buf));
191         }
192         Buf[Len++] = C;
193     } while (C != '\0');
194
195     /* Allocate an import structure and initialize it */
196     I = xmalloc (sizeof (*I) - sizeof (I->Name) + Len);
197     memcpy (I->Name, Buf, Len);
198
199     /* Return the new struct */
200     return I;
201 }
202
203
204
205 static void ReadO65RelocInfo (FILE* F, const O65Data* D, Collection* Reloc)
206 /* Read relocation data for one segment */
207 {
208     /* Relocation starts at (start address - 1) */
209     unsigned long Offs = (unsigned long) -1L;
210
211     while (1) {
212
213         O65Reloc* R;
214
215         /* Read the next relocation offset */
216         unsigned char C = Read8 (F);
217         if (C == 0) {
218             /* End of relocation table */
219             break;
220         }
221
222         /* Create a new relocation entry */
223         R = xmalloc (sizeof (*R));
224
225         /* Handle overflow bytes */
226         while (C == 0xFF) {
227             Offs += 0xFE;
228             C = Read8 (F);
229         }
230
231         /* Calculate the final offset */
232         R->Offs = (Offs += C);
233
234         /* Read typebyte and segment id */
235         C = Read8 (F);
236         R->Type   = (C & O65_RTYPE_MASK);
237         R->SegID  = (C & O65_SEGID_MASK);
238
239         /* Read an additional relocation value if there is one */
240         R->SymIdx = (R->SegID == O65_SEGID_UNDEF)? ReadO65Size (F, &D->Header) : 0;
241         switch (R->Type) {
242
243             case O65_RTYPE_HIGH:
244                 if ((D->Header.mode & O65_RELOC_MASK) == O65_RELOC_BYTE) {
245                     /* Low byte follows */
246                     R->Val = Read8 (F);
247                 } else {
248                     /* Low byte is zero */
249                     R->Val = 0;
250                 }
251                 break;
252
253             case O65_RTYPE_SEG:
254                 /* Low 16 byte of the segment address follow */
255                 R->Val = Read16 (F);
256                 break;
257
258             default:
259                 R->Val = 0;
260                 break;
261         }
262
263         /* Insert this relocation entry into the collection */
264         CollAppend (Reloc, R);
265     }
266 }
267
268
269
270 static O65Export* ReadO65Export (FILE* F, const O65Header* H)
271 /* Read an o65 export from the file */
272 {
273     O65Export* E;
274
275     /* Allow identifiers up to 511 bytes */
276     char Buf[512];
277
278     /* Read the identifier */
279     unsigned Len = 0;
280     char C;
281     do {
282         C = Read8 (F);
283         if (Len >= sizeof (Buf)) {
284             Error ("Exported identifier exceeds maximum size (%u)", sizeof (Buf));
285         }
286         Buf[Len++] = C;
287     } while (C != '\0');
288
289     /* Allocate an export structure and initialize it */
290     E = xmalloc (sizeof (*E) - sizeof (E->Name) + Len);
291     memcpy (E->Name, Buf, Len);
292     E->SegID = Read8 (F);
293     E->Val   = ReadO65Size (F, H);
294
295     /* Return the new struct */
296     return E;
297 }
298
299
300
301 static O65Data* ReadO65Data (FILE* F)
302 /* Read a complete o65 file into dynamically allocated memory and return the
303  * created O65Data struct.
304  */
305 {
306     unsigned long Count;
307     O65Option* O;
308
309     /* Create the struct we're going to return */
310     O65Data* D = NewO65Data ();
311
312     /* Read the header */
313     ReadO65Header (F, &D->Header);
314
315     /* Read the options */
316     while ((O = ReadO65Option (F)) != 0) {
317         CollAppend (&D->Options, O);
318     }
319
320     /* Allocate space for the text segment and read it */
321     D->Text = xmalloc (D->Header.tlen);
322     ReadData (F, D->Text, D->Header.tlen);
323
324     /* Allocate space for the data segment and read it */
325     D->Data = xmalloc (D->Header.dlen);
326     ReadData (F, D->Data, D->Header.dlen);
327
328     /* Read the undefined references list */
329     Count = ReadO65Size (F, &D->Header);
330     while (Count--) {
331         CollAppend (&D->Imports, ReadO65Import (F));
332     }
333
334     /* Read the relocation tables for text and data segment */
335     ReadO65RelocInfo (F, D, &D->TextReloc);
336     ReadO65RelocInfo (F, D, &D->DataReloc);
337
338     /* Read the exported globals list */
339     Count = ReadO65Size (F, &D->Header);
340     while (Count--) {
341         CollAppend (&D->Exports, ReadO65Export (F, &D->Header));
342     }
343
344     /* Return the o65 data read from the file */
345     return D;
346 }
347
348
349
350 O65Data* ReadO65File (const char* Name)
351 /* Read a complete o65 file into dynamically allocated memory and return the
352  * created O65Data struct.
353  */
354 {
355     O65Data* D;
356
357     /* Open the o65 input file */
358     FILE* F = fopen (Name, "rb");
359     if (F == 0) {
360         Error ("Cannot open `%s': %s", Name, strerror (errno));
361     }
362
363     /* Read the file data */
364     D = ReadO65Data (F);
365
366     /* Close the input file. Ignore errors since we were only reading */
367     fclose (F);
368
369     /* Return the data read */
370     return D;
371 }
372
373
374
375 const char* GetO65OSName (unsigned char OS)
376 /* Return the name of the operating system given by OS */
377 {
378     switch (OS) {
379         case O65_OS_OSA65:              return "OS/A65";
380         case O65_OS_LUNIX:              return "Lunix";
381         case O65_OS_CC65_MODULE:        return "cc65 module";
382         default:                        return "unknown";
383     }
384 }
385
386
387
388 const char* GetO65OptionText (const O65Option* O)
389 /* Return the data of the given option as a readable text. The function returns
390  * a pointer to a static buffer that is reused on the next call, so if in doubt,
391  * make a copy (and no, the function is not thread safe).
392  */
393 {
394     static char Buf[256];
395     unsigned I, J;
396
397     /* Get the length of the text */
398     unsigned Len = 0;
399     while (Len < O->Len && O->Data[Len] != '\0') {
400         ++Len;
401     }
402
403     /* Copy into the buffer converting non readable characters */
404     I = J = 0;
405     while (I < sizeof (Buf) - 1 && J < Len) {
406         if (!IsControl (O->Data[J])) {
407             Buf[I++] = O->Data[J];
408         } else {
409             Buf[I++] = '\\';
410             if (I >= sizeof (Buf) - 4) {
411                 --I;
412                 break;
413             }
414             switch (O->Data[J]) {
415                 case '\t':      Buf[I++] = 't'; break;
416                 case '\b':      Buf[I++] = 'b'; break;
417                 case '\n':      Buf[I++] = 'n'; break;
418                 case '\r':      Buf[I++] = 'r'; break;
419                 case '\v':      Buf[I++] = 'v'; break;
420                 default:
421                     sprintf (Buf + I, "x%02X", O->Data[J]);
422                     I += 3;
423                     break;
424             }
425         }
426         ++J;
427     }
428
429     /* Terminate the string and return it */
430     Buf[I] = '\0';
431     return Buf;
432 }
433
434
435