]> git.sur5r.net Git - cc65/blob - src/co65/convert.c
bf50fb539cbc5788de265cbdf6ce6c8b696d47b7
[cc65] / src / co65 / convert.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 convert.c                                 */
4 /*                                                                           */
5 /*       Actual conversion routines for the co65 object file converter       */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 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 "debugflag.h"
42 #include "print.h"
43 #include "version.h"
44 #include "xmalloc.h"
45 #include "xsprintf.h"
46
47 /* co65 */
48 #include "error.h"
49 #include "global.h"
50 #include "model.h"
51 #include "o65.h"
52 #include "convert.h"
53
54
55
56 /*****************************************************************************/
57 /*                                   Code                                    */
58 /*****************************************************************************/
59
60
61
62 static void PrintO65Stats (const O65Data* D)
63 /* Print information about the O65 file if --verbose is given */
64 {
65     Print (stdout, 1, "Size of text segment:               %5lu\n", D->Header.tlen);
66     Print (stdout, 1, "Size of data segment:               %5lu\n", D->Header.dlen);
67     Print (stdout, 1, "Size of bss segment:                %5lu\n", D->Header.blen);
68     Print (stdout, 1, "Size of zeropage segment:           %5lu\n", D->Header.zlen);
69     Print (stdout, 1, "Number of imports:                  %5u\n", CollCount (&D->Imports));
70     Print (stdout, 1, "Number of exports:                  %5u\n", CollCount (&D->Exports));
71     Print (stdout, 1, "Number of text segment relocations: %5u\n", CollCount (&D->TextReloc));
72     Print (stdout, 1, "Number of data segment relocations: %5u\n", CollCount (&D->DataReloc));
73 }
74
75
76
77 static void SetupSegLabels (FILE* F)
78 /* Setup the segment label names */
79 {
80     if (BssLabel) {
81         fprintf (F, ".export\t\t%s\n", BssLabel);
82     } else {
83         BssLabel = xstrdup ("BSS");
84     }
85     if (CodeLabel) {
86         fprintf (F, ".export\t\t%s\n", CodeLabel);
87     } else {
88         CodeLabel = xstrdup ("CODE");
89     }
90     if (DataLabel) {
91         fprintf (F, ".export\t\t%s\n", DataLabel);
92     } else {
93         DataLabel = xstrdup ("DATA");
94     }
95     if (ZeropageLabel) {
96         fprintf (F, ".export\t\t%s\n", ZeropageLabel);
97     } else {
98         ZeropageLabel = xstrdup ("ZEROPAGE");
99     }
100 }
101
102
103
104 static void ConvertImports (FILE* F, const O65Data* D)
105 /* Convert the imports */
106 {
107     unsigned I;
108
109     if (CollCount (&D->Imports) > 0) {
110         for (I = 0; I < CollCount (&D->Imports); ++I) {
111
112             /* Get the next import */
113             const O65Import* Import = CollConstAt (&D->Imports, I);
114
115             /* Import it by name */
116             fprintf (F, ".import\t%s\n", Import->Name);
117         }
118         fprintf (F, "\n");
119     }
120 }
121
122
123
124 static void ConvertExports (FILE* F, const O65Data* D)
125 /* Convert the exports */
126 {
127     unsigned I;
128
129     if (CollCount (&D->Exports) > 0) {
130         for (I = 0; I < CollCount (&D->Exports); ++I) {
131
132             /* Get the next import */
133             const O65Export* Export = CollConstAt (&D->Exports, I);
134
135             /* First define it */
136             fprintf (F, "%s = XXX\n", Export->Name);    /* ### */
137
138             /* Then export it by name */
139             fprintf (F, ".export\t%s\n", Export->Name);
140         }
141         fprintf (F, "\n");
142     }
143 }
144
145
146
147 static const char* LabelPlusOffs (const char* Label, long Offs)
148 /* Generate "Label+xxx" in a static buffer and return a pointer to the buffer */
149 {
150     static char Buf[256];
151     xsprintf (Buf, sizeof (Buf), "%s%+ld", Label, Offs);
152     return Buf;
153 }
154
155
156
157 static const char* RelocExpr (const O65Data* D, const O65Reloc* R, unsigned long Val)
158 /* Generate the segment relative relocation expression */
159 {
160     const O65Import* Import;
161
162     switch (R->SegID) {
163
164         case O65_SEGID_UNDEF:
165             if (R->SymIdx >= CollCount (&D->Imports)) {
166                 Error ("Import index out of range (input file corrupt)");
167             }
168             Import = CollConstAt (&D->Imports, R->SymIdx);
169             return LabelPlusOffs (Import->Name, Val);
170
171         case O65_SEGID_TEXT:
172             return LabelPlusOffs (CodeLabel, Val - D->Header.tbase);
173
174         case O65_SEGID_DATA:
175             return LabelPlusOffs (DataLabel, Val - D->Header.dbase);
176
177         case O65_SEGID_BSS:
178             return LabelPlusOffs (BssLabel, Val - D->Header.bbase);
179
180         case O65_SEGID_ZP:
181             return LabelPlusOffs (ZeropageLabel, Val - D->Header.zbase);
182
183         case O65_SEGID_ABS:
184             return LabelPlusOffs ("", Val);
185
186         default:
187             Internal ("Cannot handle this segment reference in reloc entry");
188     }
189
190     /* NOTREACHED */
191     return 0;
192 }
193
194
195
196 static void ConvertSeg (FILE* F, const O65Data* D, const Collection* Relocs,
197                         const unsigned char* Data, unsigned long Size)
198 /* Convert one segment */
199 {
200     const O65Reloc* R;
201     unsigned        RIdx;
202     unsigned long   Byte;
203
204     /* Get the pointer to the first relocation entry if there are any */
205     R = (CollCount (Relocs) > 0)? CollConstAt (Relocs, 0) : 0;
206
207     /* Initialize for the loop */
208     RIdx = 0;
209     Byte = 0;
210
211     /* Walk over the segment data */
212     while (Byte < Size) {
213
214         if (R && R->Offs == Byte) {
215             /* We've reached an entry that must be relocated */
216             unsigned long Val;
217             switch (R->Type) {
218
219                 case O65_RTYPE_WORD:
220                     if (Byte >= Size - 1) {
221                         Error ("Found WORD relocation, but not enough bytes left");
222                     } else {
223                         Val = (Data[Byte+1] << 8) + Data[Byte];
224                         Byte += 2;
225                         fprintf (F, "\t.word\t%s\n", RelocExpr (D, R, Val));
226                     }
227                     break;
228
229                 case O65_RTYPE_HIGH:
230                     Val = (Data[Byte++] << 8) + R->Val;
231                     fprintf (F, "\t.byte\t>(%s)\n", RelocExpr (D, R, Val));
232                     break;
233
234                 case O65_RTYPE_LOW:
235                     Val = Data[Byte++];
236                     fprintf (F, "\t.byte\t<(%s)\n", RelocExpr (D, R, Val));
237                     break;
238
239                 case O65_RTYPE_SEGADDR:
240                     if (Byte >= Size - 2) {
241                         Error ("Found SEGADDR relocation, but not enough bytes left");
242                     } else {
243                         Val = (((unsigned long) Data[Byte+2]) << 16) +
244                               (((unsigned long) Data[Byte+1]) <<  8) +
245                               (((unsigned long) Data[Byte+0]) <<  0) +
246                               R->Val;
247                         Byte += 3;
248                         fprintf (F, "\t.faraddr\t%s\n", RelocExpr (D, R, Val));
249                     }
250                     break;
251
252                 case O65_RTYPE_SEG:
253                     /* FALLTHROUGH for now */
254                 default:
255                     Internal ("Cannot handle relocation type %d at %lu",
256                               R->Type, Byte);
257             }
258
259             /* Get the next relocation entry */
260             if (++RIdx < CollCount (Relocs)) {
261                 R = CollConstAt (Relocs, RIdx);
262             } else {
263                 R = 0;
264             }
265
266         } else {
267             /* Just a constant value */
268             fprintf (F, "\t.byte\t$%02X\n", Data[Byte++]);
269         }
270     }
271
272     fprintf (F, "\n");
273 }
274
275
276
277 static void ConvertCodeSeg (FILE* F, const O65Data* D)
278 /* Do code segment conversion */
279 {
280     /* Header */
281     fprintf (F,
282              ";\n; CODE SEGMENT\n;\n"
283              ".segment\t\"%s\"\n"
284              "%s:\n",
285              CodeSeg,
286              CodeLabel);
287
288     /* Segment data */
289     ConvertSeg (F, D, &D->TextReloc, D->Text, D->Header.tlen);
290 }
291
292
293
294 static void ConvertDataSeg (FILE* F, const O65Data* D)
295 /* Do data segment conversion */
296 {
297     /* Header */
298     fprintf (F,
299              ";\n; DATA SEGMENT\n;\n"
300              ".segment\t\"%s\"\n"
301              "%s:\n",
302              DataSeg,
303              DataLabel);
304
305     /* Segment data */
306     ConvertSeg (F, D, &D->DataReloc, D->Data, D->Header.dlen);
307 }
308
309
310
311 static void ConvertBssSeg (FILE* F, const O65Data* D)
312 /* Do bss segment conversion */
313 {
314     /* Header */
315     fprintf (F,
316              ";\n; BSS SEGMENT\n;\n"
317              ".segment\t\"%s\"\n"
318              "%s:\n",
319              BssSeg,
320              BssLabel);
321
322     /* Segment data */
323     fprintf (F, "\t.res\t%lu\n", D->Header.blen);
324     fprintf (F, "\n");
325 }
326
327
328
329 static void ConvertZeropageSeg (FILE* F, const O65Data* D)
330 /* Do zeropage segment conversion */
331 {
332     /* Header */
333     fprintf (F, ";\n; ZEROPAGE SEGMENT\n;\n");
334
335     if (Model == O65_MODEL_CC65_MODULE) {
336         /* o65 files of type cc65-module are linked together with a definition
337          * file for the zero page, but the zero page is not allocated in the
338          * module itself, but the locations are mapped to the zp locations of
339          * the main file.
340          */
341         fprintf (F, ".import\t__ZP_START__\t\t; Linker generated symbol\n");
342         fprintf (F, "%s = __ZP_START__\n", ZeropageLabel);
343     } else {
344         /* Header */
345         fprintf (F, ".segment\t\"%s\", zeropage\n%s:\n", ZeropageSeg, ZeropageLabel);
346
347         /* Segment data */
348         fprintf (F, "\t.res\t%lu\n", D->Header.zlen);
349     }
350     fprintf (F, "\n");
351 }
352
353
354
355 void Convert (const O65Data* D)
356 /* Convert the o65 file in D using the given output file. */
357 {
358     FILE*       F;
359     unsigned    I;
360     char*       Author = 0;
361
362     /* For now, we do only accept o65 files generated by the ld65 linker which
363      * have a specific format.
364      */
365     if (!Debug && D->Header.mode != O65_MODE_CC65) {
366         Error ("Cannot convert o65 files of this type");
367     }
368
369     /* Output statistics */
370     PrintO65Stats (D);
371
372     /* Walk through the options and print them if verbose mode is enabled.
373      * Check for a os=cc65 option and bail out if we didn't find one (for
374      * now - later we switch to special handling).
375      */
376     for (I = 0; I < CollCount (&D->Options); ++I) {
377
378         /* Get the next option */
379         const O65Option* O = CollConstAt (&D->Options, I);
380
381         /* Check the type of the option */
382         switch (O->Type) {
383
384             case O65_OPT_FILENAME:
385                 Print (stdout, 1, "O65 filename option:         `%s'\n",
386                        GetO65OptionText (O));
387                 break;
388
389             case O65_OPT_OS:
390                 if (O->Len == 2) {
391                     Warning ("Operating system option without data found");
392                 } else {
393                     Print (stdout, 1, "O65 operating system option: `%s'\n",
394                            GetO65OSName (O->Data[0]));
395                     switch (O->Data[0]) {
396                         case O65_OS_CC65_MODULE:
397                             if (Model != O65_MODEL_NONE &&
398                                 Model != O65_MODEL_CC65_MODULE) {
399                                 Warning ("Wrong o65 model for input file specified");
400                             } else {
401                                 Model = O65_MODEL_CC65_MODULE;
402                             }
403                             break;
404                     }
405                 }
406                 break;
407
408             case O65_OPT_ASM:
409                 Print (stdout, 1, "O65 assembler option:        `%s'\n",
410                        GetO65OptionText (O));
411                 break;
412
413             case O65_OPT_AUTHOR:
414                 if (Author) {
415                     xfree (Author);
416                 }
417                 Author = xstrdup (GetO65OptionText (O));
418                 Print (stdout, 1, "O65 author option:           `%s'\n", Author);
419                 break;
420
421             case O65_OPT_TIMESTAMP:
422                 Print (stdout, 1, "O65 timestamp option:        `%s'\n",
423                        GetO65OptionText (O));
424                 break;
425
426             default:
427                 Warning ("Found unknown option, type %d, length %d",
428                          O->Type, O->Len);
429                 break;
430         }
431     }
432
433     /* If we shouldn't generate output, we're done here */
434     if (NoOutput) {
435         return;
436     }
437
438     /* Open the output file */
439     F = fopen (OutputName, "wb");
440     if (F == 0) {
441         Error ("Cannot open `%s': %s", OutputName, strerror (errno));
442     }
443
444     /* Create a header */
445     fprintf (F, ";\n; File generated by co65 v %u.%u.%u using model `%s'\n;\n",
446              VER_MAJOR, VER_MINOR, VER_PATCH, GetModelName (Model));
447
448     /* Select the CPU */
449     if ((D->Header.mode & O65_CPU_MASK) == O65_CPU_65816) {
450         fprintf (F, ".p816\n");
451     }
452
453     /* Object file options */
454     fprintf (F, ".fopt\t\tcompiler,\"co65 v %u.%u.%u\"\n",
455              VER_MAJOR, VER_MINOR, VER_PATCH);
456     if (Author) {
457         fprintf (F, ".fopt\t\tauthor, \"%s\"\n", Author);
458         xfree (Author);
459         Author = 0;
460     }
461
462     /* Several other assembler options */
463     fprintf (F, ".case\t\ton\n");
464     fprintf (F, ".debuginfo\t%s\n", (DebugInfo != 0)? "on" : "off");
465
466     /* Setup/export the segment labels */
467     SetupSegLabels (F);
468
469     /* End of header */
470     fprintf (F, "\n");
471
472     /* Imported identifiers */
473     ConvertImports (F, D);
474
475     /* Exported identifiers */
476     ConvertExports (F, D);
477
478     /* Code segment */
479     ConvertCodeSeg (F, D);
480
481     /* Data segment */
482     ConvertDataSeg (F, D);
483
484     /* BSS segment */
485     ConvertBssSeg (F, D);
486
487     /* Zero page segment */
488     ConvertZeropageSeg (F, D);
489
490     /* End of data */
491     fprintf (F, ".end\n");
492     fclose (F);
493 }
494
495
496