Mercurial > hg-old > index.cgi
comparison lwasm/output.c @ 374:d99322ef6f21
Stage 1: actually do output
author | lost@starbug |
---|---|
date | Sat, 24 Apr 2010 14:15:18 -0600 |
parents | old-trunk/lwasm/old/output.c@eb230fa7d28e |
children | 3498b2d88376 |
comparison
equal
deleted
inserted
replaced
373:8f9d72cfb897 | 374:d99322ef6f21 |
---|---|
1 /* | |
2 output.c | |
3 Copyright © 2009, 2010 William Astle | |
4 | |
5 This file is part of LWASM. | |
6 | |
7 LWASM is free software: you can redistribute it and/or modify it under the | |
8 terms of the GNU General Public License as published by the Free Software | |
9 Foundation, either version 3 of the License, or (at your option) any later | |
10 version. | |
11 | |
12 This program is distributed in the hope that it will be useful, but WITHOUT | |
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 more details. | |
16 | |
17 You should have received a copy of the GNU General Public License along with | |
18 this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 | |
21 Contains the code for actually outputting the assembled code | |
22 */ | |
23 #include <config.h> | |
24 #include <errno.h> | |
25 #include <stdio.h> | |
26 #include <string.h> | |
27 #include <unistd.h> | |
28 | |
29 #include <lw_alloc.h> | |
30 #include <lw_expr.h> | |
31 | |
32 #include "lwasm.h" | |
33 | |
34 void write_code_raw(asmstate_t *as, FILE *of); | |
35 void write_code_decb(asmstate_t *as, FILE *of); | |
36 void write_code_rawrel(asmstate_t *as, FILE *of); | |
37 void write_code_obj(asmstate_t *as, FILE *of); | |
38 void write_code_os9(asmstate_t *as, FILE *of); | |
39 | |
40 // this prevents warnings about not using the return value of fwrite() | |
41 #define writebytes(s, l, c, f) do { int r; r = fwrite((s), (l), (c), (f)); } while (0) | |
42 | |
43 void do_output(asmstate_t *as) | |
44 { | |
45 FILE *of; | |
46 | |
47 if (as -> errorcount > 0) | |
48 { | |
49 fprintf(stderr, "Not doing output due to assembly errors.\n"); | |
50 return; | |
51 } | |
52 | |
53 of = fopen(as -> output_file, "wb"); | |
54 if (!of) | |
55 { | |
56 fprintf(stderr, "Cannot open '%s' for output", as -> output_file); | |
57 perror(""); | |
58 return; | |
59 } | |
60 | |
61 switch (as -> output_format) | |
62 { | |
63 case OUTPUT_RAW: | |
64 write_code_raw(as, of); | |
65 break; | |
66 | |
67 case OUTPUT_DECB: | |
68 write_code_decb(as, of); | |
69 break; | |
70 | |
71 case OUTPUT_RAWREL: | |
72 write_code_rawrel(as, of); | |
73 break; | |
74 | |
75 case OUTPUT_OBJ: | |
76 write_code_obj(as, of); | |
77 break; | |
78 | |
79 case OUTPUT_OS9: | |
80 write_code_os9(as, of); | |
81 break; | |
82 | |
83 default: | |
84 fprintf(stderr, "BUG: unrecognized output format when generating output file\n"); | |
85 fclose(of); | |
86 unlink(as -> output_file); | |
87 return; | |
88 } | |
89 | |
90 fclose(of); | |
91 } | |
92 | |
93 /* | |
94 rawrel output treats an ORG directive as an offset from the start of the | |
95 file. Undefined results will occur if an ORG directive moves the output | |
96 pointer backward. This particular implementation uses "fseek" to handle | |
97 ORG requests and to skip over RMBs. | |
98 | |
99 This simple brain damanged method simply does an fseek before outputting | |
100 each instruction. | |
101 */ | |
102 void write_code_rawrel(asmstate_t *as, FILE *of) | |
103 { | |
104 line_t *cl; | |
105 | |
106 for (cl = as -> line_head; cl; cl = cl -> next) | |
107 { | |
108 if (cl -> outputl <= 0) | |
109 continue; | |
110 | |
111 fseek(of, lw_expr_intval(cl -> addr), SEEK_SET); | |
112 writebytes(cl -> output, cl -> outputl, 1, of); | |
113 } | |
114 } | |
115 | |
116 /* | |
117 raw merely writes all the bytes directly to the file as is. ORG is just a | |
118 reference for the assembler to handle absolute references. Multiple ORG | |
119 statements will produce mostly useless results | |
120 */ | |
121 void write_code_raw(asmstate_t *as, FILE *of) | |
122 { | |
123 line_t *cl; | |
124 | |
125 for (cl = as -> line_head; cl; cl = cl -> next) | |
126 { | |
127 if (cl -> len > 0 && cl -> outputl == 0) | |
128 { | |
129 int i; | |
130 for (i = 0; i < cl -> len; i++) | |
131 writebytes("\0", 1, 1, of); | |
132 continue; | |
133 } | |
134 else if (cl -> outputl > 0) | |
135 writebytes(cl -> output, cl -> outputl, 1, of); | |
136 } | |
137 } | |
138 | |
139 | |
140 /* | |
141 OS9 target also just writes all the bytes in order. No need for anything | |
142 else. | |
143 */ | |
144 void write_code_os9(asmstate_t *as, FILE *of) | |
145 { | |
146 line_t *cl; | |
147 | |
148 for (cl = as -> line_head; cl; cl = cl -> next) | |
149 { | |
150 if (cl -> inmod == 0) | |
151 continue; | |
152 if (cl -> len > 0 && cl -> outputl == 0) | |
153 { | |
154 int i; | |
155 for (i = 0; i < cl -> len; i++) | |
156 writebytes("\0", 1, 1, of); | |
157 continue; | |
158 } | |
159 else if (cl -> outputl > 0) | |
160 writebytes(cl -> output, cl -> outputl, 1, of); | |
161 } | |
162 } | |
163 | |
164 void write_code_decb(asmstate_t *as, FILE *of) | |
165 { | |
166 long preambloc; | |
167 line_t *cl; | |
168 int blocklen = -1; | |
169 int nextcalc = -1; | |
170 unsigned char outbuf[5]; | |
171 int caddr; | |
172 | |
173 for (cl = as -> line_head; cl; cl = cl -> next) | |
174 { | |
175 if (cl -> outputl < 0) | |
176 continue; | |
177 caddr = lw_expr_intval(cl -> addr); | |
178 if (caddr != nextcalc && cl -> outputl > 0) | |
179 { | |
180 // need preamble here | |
181 if (blocklen > 0) | |
182 { | |
183 // update previous preamble if needed | |
184 fseek(of, preambloc, SEEK_SET); | |
185 outbuf[0] = (blocklen >> 8) & 0xFF; | |
186 outbuf[1] = blocklen & 0xFF; | |
187 writebytes(outbuf, 2, 1, of); | |
188 fseek(of, 0, SEEK_END); | |
189 } | |
190 blocklen = 0; | |
191 nextcalc = caddr; | |
192 outbuf[0] = 0x00; | |
193 outbuf[1] = 0x00; | |
194 outbuf[2] = 0x00; | |
195 outbuf[3] = (nextcalc >> 8) & 0xFF; | |
196 outbuf[4] = nextcalc & 0xFF; | |
197 preambloc = ftell(of) + 1; | |
198 writebytes(outbuf, 5, 1, of); | |
199 } | |
200 nextcalc += cl -> outputl; | |
201 writebytes(cl -> output, cl -> outputl, 1, of); | |
202 blocklen += cl -> outputl; | |
203 } | |
204 if (blocklen > 0) | |
205 { | |
206 fseek(of, preambloc, SEEK_SET); | |
207 outbuf[0] = (blocklen >> 8) & 0xFF; | |
208 outbuf[1] = blocklen & 0xFF; | |
209 writebytes(outbuf, 2, 1, of); | |
210 fseek(of, 0, SEEK_END); | |
211 } | |
212 | |
213 // now write postamble | |
214 outbuf[0] = 0xFF; | |
215 outbuf[1] = 0x00; | |
216 outbuf[2] = 0x00; | |
217 outbuf[3] = (as -> execaddr >> 8) & 0xFF; | |
218 outbuf[4] = (as -> execaddr) & 0xFF; | |
219 writebytes(outbuf, 5, 1, of); | |
220 } | |
221 | |
222 void write_code_obj_sbadd(sectiontab_t *s, unsigned char b) | |
223 { | |
224 if (s -> oblen >= s -> obsize) | |
225 { | |
226 s -> obytes = lw_realloc(s -> obytes, s -> obsize + 128); | |
227 s -> obsize += 128; | |
228 } | |
229 s -> obytes[s -> oblen] = b; | |
230 s -> oblen += 1; | |
231 } | |
232 | |
233 void write_code_obj(asmstate_t *as, FILE *of) | |
234 { | |
235 line_t *l; | |
236 sectiontab_t *s; | |
237 reloctab_t *re; | |
238 struct symtabe *se; | |
239 | |
240 int i; | |
241 unsigned char buf[16]; | |
242 | |
243 // output the magic number and file header | |
244 // the 8 is NOT an error | |
245 writebytes("LWOBJ16", 8, 1, of); | |
246 | |
247 // run through the entire system and build the byte streams for each | |
248 // section; at the same time, generate a list of "local" symbols to | |
249 // output for each section | |
250 // NOTE: for "local" symbols, we will append \x01 and the ascii string | |
251 // of the context identifier (so sym in context 1 would be "sym\x011" | |
252 // we can do this because the linker can handle symbols with any | |
253 // character other than NUL. | |
254 // also we will generate a list of incomplete references for each | |
255 // section along with the actual definition that will be output | |
256 | |
257 // once all this information is generated, we will output each section | |
258 // to the file | |
259 | |
260 // NOTE: we build everything in memory then output it because the | |
261 // assembler accepts multiple instances of the same section but the | |
262 // linker expects only one instance of each section in the object file | |
263 // so we need to collect all the various pieces of a section together | |
264 // (also, the assembler treated multiple instances of the same section | |
265 // as continuations of previous sections so we would need to collect | |
266 // them together anyway. | |
267 | |
268 for (l = as -> line_head; l; l = l -> next) | |
269 { | |
270 if (l -> csect) | |
271 { | |
272 // we're in a section - need to output some bytes | |
273 if (l -> outputl > 0) | |
274 for (i = 0; i < l -> outputl; i++) | |
275 write_code_obj_sbadd(l -> csect, l -> output[i]); | |
276 else if (l -> outputl == 0) | |
277 for (i = 0; i < l -> len; i++) | |
278 write_code_obj_sbadd(l -> csect, 0); | |
279 } | |
280 } | |
281 | |
282 // run through the sections | |
283 for (s = as -> sections; s; s = s -> next) | |
284 { | |
285 // write the name | |
286 writebytes(s -> name, strlen(s -> name) + 1, 1, of); | |
287 | |
288 // write the flags | |
289 if (s -> flags & section_flag_bss) | |
290 writebytes("\x01", 1, 1, of); | |
291 | |
292 // indicate end of flags - the "" is NOT an error | |
293 writebytes("", 1, 1, of); | |
294 | |
295 // now the local symbols | |
296 for (se = as -> symtab.head; se; se = se -> next) | |
297 { | |
298 // ignore symbols not in this section | |
299 if (se -> section != s) | |
300 continue; | |
301 | |
302 if (se -> flags & symbol_flag_set) | |
303 continue; | |
304 | |
305 // don't output non-constant symbols | |
306 if (!lw_expr_istype(se -> value, lw_expr_type_int)) | |
307 continue; | |
308 | |
309 writebytes(se -> symbol, strlen(se -> symbol), 1, of); | |
310 if (se -> context >= 0) | |
311 { | |
312 writebytes("\x01", 1, 1, of); | |
313 sprintf(buf, "%d", se -> context); | |
314 writebytes(buf, strlen(buf), 1, of); | |
315 } | |
316 // the "" is NOT an error | |
317 writebytes("", 1, 1, of); | |
318 | |
319 // write the address | |
320 buf[0] = (lw_expr_intval(se -> value) >> 8) & 0xff; | |
321 buf[1] = lw_expr_intval(se -> value) & 0xff; | |
322 writebytes(buf, 2, 1, of); | |
323 } | |
324 // flag end of local symbol table - "" is NOT an error | |
325 writebytes("", 1, 1, of); | |
326 | |
327 // now the exports -- FIXME | |
328 /* for (ex = as -> exportlist; ex; ex = ex -> next) | |
329 { | |
330 int eval; | |
331 ex -> se -> section != s) | |
332 continue; | |
333 if (!lwasm_expr_exportable(ex -> se -> value)) | |
334 continue; | |
335 eval = lwasm_expr_exportval(ex -> se -> value); | |
336 writebytes(ex -> symbol, strlen(ex -> symbol) + 1, 1, of); | |
337 buf[0] = (eval >> 8) & 0xff; | |
338 buf[1] = eval & 0xff; | |
339 writebytes(buf, 2, 1, of); | |
340 } | |
341 */ | |
342 // flag end of exported symbols - "" is NOT an error | |
343 writebytes("", 1, 1, of); | |
344 | |
345 // FIXME - relocation table | |
346 /* for (re = s -> rl; re; re = re -> next) | |
347 { | |
348 if (re -> expr == NULL) | |
349 { | |
350 // this is an error but we'll simply ignore it | |
351 // and not output this expression | |
352 continue; | |
353 } | |
354 | |
355 // work through each term in the expression and output | |
356 // the proper equivalent to the object file | |
357 if (re -> relocsize == 1) | |
358 { | |
359 // flag an 8 bit relocation (low 8 bits will be used) | |
360 buf[0] = 0xFF; | |
361 buf[1] = 0x01; | |
362 writebytes(buf, 2, 1, of); | |
363 } | |
364 for (sn = re -> expr -> head; sn; sn = sn -> next) | |
365 { | |
366 switch (sn -> term -> term_type) | |
367 { | |
368 case LWASM_TERM_OPER: | |
369 buf[0] = 0x04; | |
370 buf[1] = sn -> term -> value; | |
371 writebytes(buf, 2, 1, of); | |
372 break; | |
373 | |
374 case LWASM_TERM_INT: | |
375 buf[0] = 0x01; | |
376 buf[1] = (sn -> term -> value >> 8) & 0xff; | |
377 buf[2] = sn -> term -> value & 0xff; | |
378 writebytes(buf, 3, 1, of); | |
379 break; | |
380 | |
381 case LWASM_TERM_SECBASE: | |
382 writebytes("\x05", 1, 1, of); | |
383 break; | |
384 | |
385 case LWASM_TERM_SYM: | |
386 // now for the ugly part - resolve a symbol reference | |
387 // and determine whether it's internal, external, or | |
388 // a section base | |
389 se = lwasm_find_symbol(as, sn -> term -> symbol, re -> context); | |
390 if (!se) | |
391 se = lwasm_find_symbol(as, sn -> term -> symbol, -1); | |
392 if (!se || se -> flags & SYMBOL_EXTERN) | |
393 { | |
394 // not found - assume external reference | |
395 // found but flagged external - handle it | |
396 writebytes("\x02", 1, 1, of); | |
397 writebytes(se -> sym, strlen(se -> sym) + 1, 1, of); | |
398 break; | |
399 } | |
400 // a local symbol reference here | |
401 writebytes("\x03", 1, 1, of); | |
402 writebytes(se -> sym, strlen(se -> sym), 1, of); | |
403 if (se -> context >= 0) | |
404 { | |
405 writebytes("\x01", 1, 1, of); | |
406 sprintf(buf, "%d", se -> context); | |
407 writebytes(buf, strlen(buf), 1, of); | |
408 } | |
409 writebytes("", 1, 1, of); | |
410 break; | |
411 | |
412 default: | |
413 // unrecognized term type - replace with integer 0 | |
414 buf[0] = 0x01; | |
415 buf[1] = 0x00; | |
416 buf[2] = 0x00; | |
417 writebytes(buf, 3, 1, of); | |
418 break; | |
419 } | |
420 } | |
421 | |
422 // flag end of expressions | |
423 writebytes("", 1, 1, of); | |
424 | |
425 // write the offset | |
426 buf[0] = (re -> offset >> 8) & 0xff; | |
427 buf[1] = re -> offset & 0xff; | |
428 writebytes(buf, 2, 1, of); | |
429 } | |
430 */ | |
431 // flag end of incomplete references list | |
432 writebytes("", 1, 1, of); | |
433 | |
434 // now blast out the code | |
435 | |
436 // length | |
437 buf[0] = s -> oblen >> 8 & 0xff; | |
438 buf[1] = s -> oblen & 0xff; | |
439 writebytes(buf, 2, 1, of); | |
440 | |
441 if (!(s -> flags & section_flag_bss)) | |
442 { | |
443 writebytes(s -> obytes, s -> oblen, 1, of); | |
444 } | |
445 } | |
446 | |
447 // flag no more sections | |
448 // the "" is NOT an error | |
449 writebytes("", 1, 1, of); | |
450 } |