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