1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* output.c */ |
4 | /* */ |
5 | /* Disassembler output routines */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2000-2014, 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 <stdarg.h> |
38 | #include <string.h> |
39 | #include <ctype.h> |
40 | #include <errno.h> |
41 | |
42 | /* common */ |
43 | #include "addrsize.h" |
44 | #include "cpu.h" |
45 | #include "version.h" |
46 | |
47 | /* da65 */ |
48 | #include "code.h" |
49 | #include "error.h" |
50 | #include "global.h" |
51 | #include "output.h" |
52 | |
53 | |
54 | |
55 | /*****************************************************************************/ |
56 | /* Data */ |
57 | /*****************************************************************************/ |
58 | |
59 | |
60 | |
61 | static FILE* F = 0; /* Output stream */ |
62 | static unsigned Col = 1; /* Current column */ |
63 | static unsigned Line = 0; /* Current line on page */ |
64 | static unsigned Page = 1; /* Current output page */ |
65 | |
66 | static const char* SegmentName = 0; /* Name of current segment */ |
67 | |
68 | |
69 | |
70 | /*****************************************************************************/ |
71 | /* Code */ |
72 | /*****************************************************************************/ |
73 | |
74 | |
75 | |
76 | static void (void) |
77 | /* Print a page header */ |
78 | { |
79 | fprintf (F, |
80 | "; da65 V%s\n" |
81 | "; Created: %s\n" |
82 | "; Input file: %s\n" |
83 | "; Page: %u\n\n" , |
84 | GetVersionAsString (), |
85 | Now, |
86 | InFile, |
87 | Page); |
88 | } |
89 | |
90 | |
91 | |
92 | void OpenOutput (const char* Name) |
93 | /* Open the given file for output */ |
94 | { |
95 | /* If we have a name given, open the output file, otherwise use stdout */ |
96 | if (Name != 0) { |
97 | F = fopen (Name, "w" ); |
98 | if (F == 0) { |
99 | Error ("Cannot open '%s': %s" , Name, strerror (errno)); |
100 | } |
101 | } else { |
102 | F = stdout; |
103 | } |
104 | |
105 | /* Output the header and initialize stuff */ |
106 | PageHeader (); |
107 | Line = 5; |
108 | Col = 1; |
109 | } |
110 | |
111 | |
112 | |
113 | void CloseOutput (void) |
114 | /* Close the output file */ |
115 | { |
116 | if (F != stdout && fclose (F) != 0) { |
117 | Error ("Error closing output file: %s" , strerror (errno)); |
118 | } |
119 | } |
120 | |
121 | |
122 | |
123 | void Output (const char* Format, ...) |
124 | /* Write to the output file */ |
125 | { |
126 | if (Pass == PassCount) { |
127 | va_list ap; |
128 | va_start (ap, Format); |
129 | Col += vfprintf (F, Format, ap); |
130 | va_end (ap); |
131 | } |
132 | } |
133 | |
134 | |
135 | |
136 | void Indent (unsigned N) |
137 | /* Make sure the current line column is at position N (zero based) */ |
138 | { |
139 | if (Pass == PassCount) { |
140 | while (Col < N) { |
141 | fputc (' ', F); |
142 | ++Col; |
143 | } |
144 | } |
145 | } |
146 | |
147 | |
148 | |
149 | void LineFeed (void) |
150 | /* Add a linefeed to the output file */ |
151 | { |
152 | if (Pass == PassCount) { |
153 | fputc ('\n', F); |
154 | if (PageLength > 0 && ++Line >= PageLength) { |
155 | if (FormFeeds) { |
156 | fputc ('\f', F); |
157 | } |
158 | ++Page; |
159 | PageHeader (); |
160 | Line = 5; |
161 | } |
162 | Col = 1; |
163 | } |
164 | } |
165 | |
166 | |
167 | |
168 | void DefLabel (const char* Name) |
169 | /* Define a label with the given name */ |
170 | { |
171 | Output ("%s:" , Name); |
172 | /* If the label is longer than the configured maximum, or if it runs into |
173 | ** the opcode column, start a new line. |
174 | */ |
175 | if (Col > LBreak+2 || Col > MCol) { |
176 | LineFeed (); |
177 | } |
178 | } |
179 | |
180 | |
181 | |
182 | void DefForward (const char* Name, const char* , unsigned Offs) |
183 | /* Define a label as "* + x", where x is the offset relative to the |
184 | ** current PC. |
185 | */ |
186 | { |
187 | if (Pass == PassCount) { |
188 | /* Flush existing output if necessary */ |
189 | if (Col > 1) { |
190 | LineFeed (); |
191 | } |
192 | |
193 | /* Output the forward definition */ |
194 | Output ("%s" , Name); |
195 | Indent (ACol); |
196 | if (UseHexOffs) { |
197 | Output (":= * + $%04X" , Offs); |
198 | } else { |
199 | Output (":= * + %u" , Offs); |
200 | } |
201 | if (Comment) { |
202 | Indent (CCol); |
203 | Output ("; %s" , Comment); |
204 | } |
205 | LineFeed (); |
206 | } |
207 | } |
208 | |
209 | |
210 | |
211 | void DefConst (const char* Name, const char* , unsigned Addr) |
212 | /* Define an address constant */ |
213 | { |
214 | if (Pass == PassCount) { |
215 | Output ("%s" , Name); |
216 | Indent (ACol); |
217 | Output (":= $%04X" , Addr); |
218 | if (Comment) { |
219 | Indent (CCol); |
220 | Output ("; %s" , Comment); |
221 | } |
222 | LineFeed (); |
223 | } |
224 | } |
225 | |
226 | |
227 | |
228 | void DataByteLine (unsigned ByteCount) |
229 | /* Output a line with bytes */ |
230 | { |
231 | unsigned I; |
232 | |
233 | Indent (MCol); |
234 | Output (".byte" ); |
235 | Indent (ACol); |
236 | for (I = 0; I < ByteCount; ++I) { |
237 | if (I > 0) { |
238 | Output (",$%02X" , CodeBuf[PC+I]); |
239 | } else { |
240 | Output ("$%02X" , CodeBuf[PC+I]); |
241 | } |
242 | } |
243 | LineComment (PC, ByteCount); |
244 | LineFeed (); |
245 | } |
246 | |
247 | |
248 | |
249 | void DataDByteLine (unsigned ByteCount) |
250 | /* Output a line with dbytes */ |
251 | { |
252 | unsigned I; |
253 | |
254 | Indent (MCol); |
255 | Output (".dbyt" ); |
256 | Indent (ACol); |
257 | for (I = 0; I < ByteCount; I += 2) { |
258 | if (I > 0) { |
259 | Output (",$%04X" , GetCodeDByte (PC+I)); |
260 | } else { |
261 | Output ("$%04X" , GetCodeDByte (PC+I)); |
262 | } |
263 | } |
264 | LineComment (PC, ByteCount); |
265 | LineFeed (); |
266 | } |
267 | |
268 | |
269 | |
270 | void DataWordLine (unsigned ByteCount) |
271 | /* Output a line with words */ |
272 | { |
273 | unsigned I; |
274 | |
275 | Indent (MCol); |
276 | Output (".word" ); |
277 | Indent (ACol); |
278 | for (I = 0; I < ByteCount; I += 2) { |
279 | if (I > 0) { |
280 | Output (",$%04X" , GetCodeWord (PC+I)); |
281 | } else { |
282 | Output ("$%04X" , GetCodeWord (PC+I)); |
283 | } |
284 | } |
285 | LineComment (PC, ByteCount); |
286 | LineFeed (); |
287 | } |
288 | |
289 | |
290 | |
291 | void DataDWordLine (unsigned ByteCount) |
292 | /* Output a line with dwords */ |
293 | { |
294 | unsigned I; |
295 | |
296 | Indent (MCol); |
297 | Output (".dword" ); |
298 | Indent (ACol); |
299 | for (I = 0; I < ByteCount; I += 4) { |
300 | if (I > 0) { |
301 | Output (",$%08lX" , GetCodeDWord (PC+I)); |
302 | } else { |
303 | Output ("$%08lX" , GetCodeDWord (PC+I)); |
304 | } |
305 | } |
306 | LineComment (PC, ByteCount); |
307 | LineFeed (); |
308 | } |
309 | |
310 | |
311 | |
312 | void SeparatorLine (void) |
313 | /* Print a separator line */ |
314 | { |
315 | if (Pass == PassCount && Comments >= 1) { |
316 | Output ("; ----------------------------------------------------------------------------" ); |
317 | LineFeed (); |
318 | } |
319 | } |
320 | |
321 | |
322 | |
323 | void StartSegment (const char* Name, unsigned AddrSize) |
324 | /* Start a segment */ |
325 | { |
326 | if (Pass == PassCount) { |
327 | LineFeed (); |
328 | Output (".segment" ); |
329 | Indent (ACol); |
330 | SegmentName = Name; |
331 | Output ("\"%s\"" , Name); |
332 | if (AddrSize != ADDR_SIZE_DEFAULT) { |
333 | Output (": %s" , AddrSizeToStr (AddrSize)); |
334 | } |
335 | LineFeed (); |
336 | LineFeed (); |
337 | } |
338 | } |
339 | |
340 | |
341 | |
342 | void EndSegment (void) |
343 | /* End a segment */ |
344 | { |
345 | LineFeed (); |
346 | Output ("; End of \"%s\" segment" , SegmentName); |
347 | LineFeed (); |
348 | SeparatorLine (); |
349 | Output (".code" ); |
350 | LineFeed (); |
351 | LineFeed (); |
352 | } |
353 | |
354 | |
355 | |
356 | void (const char* ) |
357 | /* Output a comment line */ |
358 | { |
359 | Output ("; %s" , Comment); |
360 | LineFeed (); |
361 | } |
362 | |
363 | |
364 | |
365 | void (unsigned PC, unsigned Count) |
366 | /* Add a line comment with the PC and data bytes */ |
367 | { |
368 | unsigned I; |
369 | |
370 | if (Pass == PassCount && Comments >= 2) { |
371 | Indent (CCol); |
372 | Output ("; %04X" , PC); |
373 | if (Comments >= 3) { |
374 | for (I = 0; I < Count; ++I) { |
375 | Output (" %02X" , CodeBuf [PC+I]); |
376 | } |
377 | if (Comments >= 4) { |
378 | Indent (TCol); |
379 | for (I = 0; I < Count; ++I) { |
380 | unsigned char C = CodeBuf [PC+I]; |
381 | if (!isprint (C)) { |
382 | C = '.'; |
383 | } |
384 | Output ("%c" , C); |
385 | } |
386 | } |
387 | } |
388 | } |
389 | } |
390 | |
391 | |
392 | |
393 | void OutputSettings (void) |
394 | /* Output CPU and other settings */ |
395 | { |
396 | LineFeed (); |
397 | Indent (MCol); |
398 | Output (".setcpu" ); |
399 | Indent (ACol); |
400 | Output ("\"%s\"" , CPUNames[CPU]); |
401 | LineFeed (); |
402 | LineFeed (); |
403 | } |
404 | |