]> git.sur5r.net Git - cc65/blob - src/co65/o65.c
Changed most "backticks" (grave accents) into apostrophes.
[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-2004 Ullrich von Bassewitz                                       */
10 /*               Roemerstrasse 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     unsigned long Size = 0;     /* Initialize to avoid warnings */
90     switch (H->mode & O65_SIZE_MASK) {
91         case O65_SIZE_32BIT:    Size = Read32 (F);      break;
92         case O65_SIZE_16BIT:    Size = Read16 (F);      break;
93         default:                Internal ("Invalid size field value in o65 header");
94     }
95     return Size;
96 }
97
98
99
100 static void ReadO65Header (FILE* F, O65Header* H)
101 /* Read an o65 header from the given file. The function will call Error if
102 ** something is wrong.
103 */
104 {
105     static const char Magic[3] = {
106         O65_MAGIC_0, O65_MAGIC_1, O65_MAGIC_2   /* "o65" */
107     };
108
109     /* Read the marker and check it */
110     ReadData (F, H->marker, sizeof (H->marker));
111     if (H->marker[0] != O65_MARKER_0 || H->marker[1] != O65_MARKER_1) {
112         Error ("Not an o65 object file: Invalid marker %02X %02X",
113                H->marker[0], H->marker[1]);
114     }
115
116     /* Read the magic and check it */
117     ReadData (F, H->magic, sizeof (H->magic));
118     if (memcmp (H->magic, Magic, sizeof (H->magic)) != 0) {
119         Error ("Not an o65 object file: Invalid magic %02X %02X %02X",
120                H->magic[0], H->magic[1], H->magic[2]);
121     }
122
123     /* Read the version number and check it */
124     H->version = Read8 (F);
125     if (H->version != O65_VERSION) {
126         Error ("Invalid o65 version number: %02X", H->version);
127     }
128
129     /* Read the mode word */
130     H->mode = Read16 (F);
131
132     /* Read the remainder of the header */
133     H->tbase = ReadO65Size (F, H);
134     H->tlen  = ReadO65Size (F, H);
135     H->dbase = ReadO65Size (F, H);
136     H->dlen  = ReadO65Size (F, H);
137     H->bbase = ReadO65Size (F, H);
138     H->blen  = ReadO65Size (F, H);
139     H->zbase = ReadO65Size (F, H);
140     H->zlen  = ReadO65Size (F, H);
141     H->stack = ReadO65Size (F, H);
142 }
143
144
145
146 static O65Option* ReadO65Option (FILE* F)
147 /* Read the next O65 option from the given file. The option is stored into a
148 ** dynamically allocated O65Option struct which is returned. On end of options,
149 ** NULL is returned. On error, Error is called which terminates the program.
150 */
151 {
152     O65Option* O;
153
154     /* Read the length of the option and bail out on end of options */
155     unsigned char Len = Read8 (F);
156     if (Len == 0) {
157         return 0;
158     }
159     if (Len < 2) {
160         Error ("Found option with length < 2 (input file corrupt)");
161     }
162     Len -= 2;
163
164     /* Allocate a new O65Option structure of the needed size */
165     O = xmalloc (sizeof (*O) - sizeof (O->Data) + Len);
166
167     /* Assign the length and read the remaining option data */
168     O->Len  = Len;
169     O->Type = Read8 (F);
170     ReadData (F, O->Data, Len);
171
172     /* Return the new struct */
173     return O;
174 }
175
176
177
178 static O65Import* ReadO65Import (FILE* F)
179 /* Read an o65 import from the file */
180 {
181     O65Import* I;
182
183     /* Allow identifiers up to 511 bytes */
184     char Buf[512];
185
186     /* Read the identifier */
187     unsigned Len = 0;
188     char C;
189     do {
190         C = Read8 (F);
191         if (Len >= sizeof (Buf)) {
192             Error ("Imported identifier exceeds maximum size (%u)",
193                    (unsigned) sizeof (Buf));
194         }
195         Buf[Len++] = C;
196     } while (C != '\0');
197
198     /* Allocate an import structure and initialize it */
199     I = xmalloc (sizeof (*I) - sizeof (I->Name) + Len);
200     memcpy (I->Name, Buf, Len);
201
202     /* Return the new struct */
203     return I;
204 }
205
206
207
208 static void ReadO65RelocInfo (FILE* F, const O65Data* D, Collection* Reloc)
209 /* Read relocation data for one segment */
210 {
211     /* Relocation starts at (start address - 1) */
212     unsigned long Offs = (unsigned long) -1L;
213
214     while (1) {
215
216         O65Reloc* R;
217
218         /* Read the next relocation offset */
219         unsigned char C = Read8 (F);
220         if (C == 0) {
221             /* End of relocation table */
222             break;
223         }
224
225         /* Create a new relocation entry */
226         R = xmalloc (sizeof (*R));
227
228         /* Handle overflow bytes */
229         while (C == 0xFF) {
230             Offs += 0xFE;
231             C = Read8 (F);
232         }
233
234         /* Calculate the final offset */
235         R->Offs = (Offs += C);
236
237         /* Read typebyte and segment id */
238         C = Read8 (F);
239         R->Type   = (C & O65_RTYPE_MASK);
240         R->SegID  = (C & O65_SEGID_MASK);
241
242         /* Read an additional relocation value if there is one */
243         R->SymIdx = (R->SegID == O65_SEGID_UNDEF)? ReadO65Size (F, &D->Header) : 0;
244         switch (R->Type) {
245
246             case O65_RTYPE_HIGH:
247                 if ((D->Header.mode & O65_RELOC_MASK) == O65_RELOC_BYTE) {
248                     /* Low byte follows */
249                     R->Val = Read8 (F);
250                 } else {
251                     /* Low byte is zero */
252                     R->Val = 0;
253                 }
254                 break;
255
256             case O65_RTYPE_SEG:
257                 /* Low 16 byte of the segment address follow */
258                 R->Val = Read16 (F);
259                 break;
260
261             default:
262                 R->Val = 0;
263                 break;
264         }
265
266         /* Insert this relocation entry into the collection */
267         CollAppend (Reloc, R);
268     }
269 }
270
271
272
273 static O65Export* ReadO65Export (FILE* F, const O65Header* H)
274 /* Read an o65 export from the file */
275 {
276     O65Export* E;
277
278     /* Allow identifiers up to 511 bytes */
279     char Buf[512];
280
281     /* Read the identifier */
282     unsigned Len = 0;
283     char C;
284     do {
285         C = Read8 (F);
286         if (Len >= sizeof (Buf)) {
287             Error ("Exported identifier exceeds maximum size (%u)",
288                    (unsigned) sizeof (Buf));
289         }
290         Buf[Len++] = C;
291     } while (C != '\0');
292
293     /* Allocate an export structure and initialize it */
294     E = xmalloc (sizeof (*E) - sizeof (E->Name) + Len);
295     memcpy (E->Name, Buf, Len);
296     E->SegID = Read8 (F);
297     E->Val   = ReadO65Size (F, H);
298
299     /* Return the new struct */
300     return E;
301 }
302
303
304
305 static O65Data* ReadO65Data (FILE* F)
306 /* Read a complete o65 file into dynamically allocated memory and return the
307 ** created O65Data struct.
308 */
309 {
310     unsigned long Count;
311     O65Option* O;
312
313     /* Create the struct we're going to return */
314     O65Data* D = NewO65Data ();
315
316     /* Read the header */
317     ReadO65Header (F, &D->Header);
318
319     /* Read the options */
320     while ((O = ReadO65Option (F)) != 0) {
321         CollAppend (&D->Options, O);
322     }
323
324     /* Allocate space for the text segment and read it */
325     D->Text = xmalloc (D->Header.tlen);
326     ReadData (F, D->Text, D->Header.tlen);
327
328     /* Allocate space for the data segment and read it */
329     D->Data = xmalloc (D->Header.dlen);
330     ReadData (F, D->Data, D->Header.dlen);
331
332     /* Read the undefined references list */
333     Count = ReadO65Size (F, &D->Header);
334     while (Count--) {
335         CollAppend (&D->Imports, ReadO65Import (F));
336     }
337
338     /* Read the relocation tables for text and data segment */
339     ReadO65RelocInfo (F, D, &D->TextReloc);
340     ReadO65RelocInfo (F, D, &D->DataReloc);
341
342     /* Read the exported globals list */
343     Count = ReadO65Size (F, &D->Header);
344     while (Count--) {
345         CollAppend (&D->Exports, ReadO65Export (F, &D->Header));
346     }
347
348     /* Return the o65 data read from the file */
349     return D;
350 }
351
352
353
354 O65Data* ReadO65File (const char* Name)
355 /* Read a complete o65 file into dynamically allocated memory and return the
356 ** created O65Data struct.
357 */
358 {
359     O65Data* D;
360
361     /* Open the o65 input file */
362     FILE* F = fopen (Name, "rb");
363     if (F == 0) {
364         Error ("Cannot open '%s': %s", Name, strerror (errno));
365     }
366
367     /* Read the file data */
368     D = ReadO65Data (F);
369
370     /* Close the input file. Ignore errors since we were only reading */
371     fclose (F);
372
373     /* Return the data read */
374     return D;
375 }
376
377
378
379 const char* GetO65OSName (unsigned char OS)
380 /* Return the name of the operating system given by OS */
381 {
382     switch (OS) {
383         case O65_OS_OSA65:              return "OS/A65";
384         case O65_OS_LUNIX:              return "Lunix";
385         case O65_OS_CC65_MODULE:        return "cc65 module";
386         default:                        return "unknown";
387     }
388 }
389
390
391
392 const char* GetO65OptionText (const O65Option* O)
393 /* Return the data of the given option as a readable text. The function returns
394 ** a pointer to a static buffer that is reused on the next call, so if in doubt,
395 ** make a copy (and no, the function is not thread safe).
396 */
397 {
398     static char Buf[256];
399     unsigned I, J;
400
401     /* Get the length of the text */
402     unsigned Len = 0;
403     while (Len < O->Len && O->Data[Len] != '\0') {
404         ++Len;
405     }
406
407     /* Copy into the buffer converting non readable characters */
408     I = J = 0;
409     while (I < sizeof (Buf) - 1 && J < Len) {
410         if (!IsControl (O->Data[J])) {
411             Buf[I++] = O->Data[J];
412         } else {
413             Buf[I++] = '\\';
414             if (I >= sizeof (Buf) - 4) {
415                 --I;
416                 break;
417             }
418             switch (O->Data[J]) {
419                 case '\t':      Buf[I++] = 't'; break;
420                 case '\b':      Buf[I++] = 'b'; break;
421                 case '\n':      Buf[I++] = 'n'; break;
422                 case '\r':      Buf[I++] = 'r'; break;
423                 case '\v':      Buf[I++] = 'v'; break;
424                 default:
425                     sprintf (Buf + I, "x%02X", O->Data[J]);
426                     I += 3;
427                     break;
428             }
429         }
430         ++J;
431     }
432
433     /* Terminate the string and return it */
434     Buf[I] = '\0';
435     return Buf;
436 }