move around - flatter.
[framework/uifw/embryo.git] / src / bin / embryo_cc_sc6.c
1 /*  Small compiler - Binary code generation (the "assembler")
2  *
3  *  Copyright (c) ITB CompuPhase, 1997-2003
4  *
5  *  This software is provided "as-is", without any express or implied warranty.
6  *  In no event will the authors be held liable for any damages arising from
7  *  the use of this software.
8  *
9  *  Permission is granted to anyone to use this software for any purpose,
10  *  including commercial applications, and to alter it and redistribute it
11  *  freely, subject to the following restrictions:
12  *
13  *  1.  The origin of this software must not be misrepresented; you must not
14  *      claim that you wrote the original software. If you use this software in
15  *      a product, an acknowledgment in the product documentation would be
16  *      appreciated but is not required.
17  *  2.  Altered source versions must be plainly marked as such, and must not be
18  *      misrepresented as being the original software.
19  *  3.  This notice may not be removed or altered from any source distribution.
20  *
21  *  Version: $Id$
22  */
23
24 /*
25  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>             /* for macro max() */
35 #include <string.h>
36 #include <ctype.h>
37 #include "embryo_cc_osdefs.h"
38 #include "embryo_cc_sc.h"
39
40 typedef             cell(*OPCODE_PROC) (FILE * fbin, char *params, cell opcode);
41
42 typedef struct
43 {
44    cell                opcode;
45    char               *name;
46    int                 segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */
47    OPCODE_PROC         func;
48 } OPCODE;
49
50 static cell         codeindex;  /* similar to "code_idx" */
51 static cell        *lbltab;     /* label table */
52 static int          writeerror;
53 static int          bytes_in, bytes_out;
54
55 /* apparently, strtol() does not work correctly on very large (unsigned)
56  * hexadecimal values */
57 static ucell
58 hex2long(char *s, char **n)
59 {
60    unsigned long       result = 0L;
61    int                 negate = FALSE;
62    int                 digit;
63
64    /* ignore leading whitespace */
65    while (*s == ' ' || *s == '\t')
66       s++;
67
68    /* allow a negation sign to create the two's complement of numbers */
69    if (*s == '-')
70      {
71         negate = TRUE;
72         s++;
73      }                          /* if */
74
75    assert((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f')
76           || (*s >= 'a' && *s <= 'f'));
77    for (;;)
78      {
79         if (*s >= '0' && *s <= '9')
80            digit = *s - '0';
81         else if (*s >= 'a' && *s <= 'f')
82            digit = *s - 'a' + 10;
83         else if (*s >= 'A' && *s <= 'F')
84            digit = *s - 'A' + 10;
85         else
86            break;               /* probably whitespace */
87         result = (result << 4) | digit;
88         s++;
89      }                          /* for */
90    if (n != NULL)
91       *n = s;
92    if (negate)
93       result = (~result) + 1;   /* take two's complement of the result */
94    return (ucell) result;
95 }
96
97 #ifdef WORDS_BIGENDIAN
98 static short       *
99 align16(short *v)
100 {
101    unsigned char      *s = (unsigned char *)v;
102    unsigned char       t;
103
104    /* swap two bytes */
105    t = s[0];
106    s[0] = s[1];
107    s[1] = t;
108    return v;
109 }
110
111 static long        *
112 align32(long *v)
113 {
114    unsigned char      *s = (unsigned char *)v;
115    unsigned char       t;
116
117    /* swap outer two bytes */
118    t = s[0];
119    s[0] = s[3];
120    s[3] = t;
121    /* swap inner two bytes */
122    t = s[1];
123    s[1] = s[2];
124    s[2] = t;
125    return v;
126 }
127 #if defined BIT16
128 #define aligncell(v)  align16(v)
129 #else
130 #define aligncell(v)  align32(v)
131 #endif
132 #else
133 #define align16(v)    (v)
134 #define align32(v)    (v)
135 #define aligncell(v)  (v)
136 #endif
137
138 static char        *
139 skipwhitespace(char *str)
140 {
141    while (isspace(*str))
142       str++;
143    return str;
144 }
145
146 static char        *
147 stripcomment(char *str)
148 {
149    char               *ptr = strchr(str, ';');
150
151    if (ptr != NULL)
152      {
153         *ptr++ = '\n';          /* terminate the line, but leave the '\n' */
154         *ptr = '\0';
155      }                          /* if */
156    return str;
157 }
158
159 static void
160 write_encoded(FILE * fbin, ucell * c, int num)
161 {
162    assert(sizeof(cell) <= 4);   /* code must be adjusted for larger cells */
163    assert(fbin != NULL);
164    while (num-- > 0)
165      {
166         if (sc_compress)
167           {
168              ucell               p = (ucell) * c;
169              unsigned char       t[5];  /* a 32-bit cell is encoded in max. 5 bytes (3 bytes for a 16-bit cell) */
170              unsigned char       code;
171              int                 index;
172
173              for (index = 0; index < 5; index++)
174                {
175                   t[index] = (unsigned char)(p & 0x7f); /* store 7 bits */
176                   p >>= 7;
177                }                /* for */
178              /* skip leading zeros */
179              while (index > 1 && t[index - 1] == 0
180                     && (t[index - 2] & 0x40) == 0)
181                 index--;
182              /* skip leading -1s *//* ??? for BIT16, check for index==3 && t[index-1]==0x03 */
183              if (index == 5 && t[index - 1] == 0x0f
184                  && (t[index - 2] & 0x40) != 0)
185                 index--;
186              while (index > 1 && t[index - 1] == 0x7f
187                     && (t[index - 2] & 0x40) != 0)
188                 index--;
189              /* write high byte first, write continuation bits */
190              assert(index > 0);
191              while (index-- > 0)
192                {
193                   code =
194                      (unsigned char)((index == 0) ? t[index]
195                                      : (t[index] | 0x80));
196                   writeerror |= !sc_writebin(fbin, &code, 1);
197                   bytes_out++;
198                }                /* while */
199              bytes_in += sizeof *c;
200              assert(AMX_EXPANDMARGIN > 2);
201              if (bytes_out - bytes_in >= AMX_EXPANDMARGIN - 2)
202                 error(106);     /* compression buffer overflow */
203           }
204         else
205           {
206              assert((sc_lengthbin(fbin) % sizeof(cell)) == 0);
207              writeerror |= !sc_writebin(fbin, aligncell(c), sizeof *c);
208           }                     /* if */
209         c++;
210      }                          /* while */
211 }
212
213 #if defined __BORLANDC__ || defined __WATCOMC__
214 #pragma argsused
215 #endif
216
217 static cell
218 noop(FILE * fbin __UNUSED__, char *params __UNUSED__, cell opcode __UNUSED__)
219 {
220    return 0;
221 }
222
223 #if defined __BORLANDC__ || defined __WATCOMC__
224 #pragma argsused
225 #endif
226
227 static cell
228 parm0(FILE * fbin, char *params __UNUSED__, cell opcode)
229 {
230    if (fbin != NULL)
231       write_encoded(fbin, (ucell *) & opcode, 1);
232    return opcodes(1);
233 }
234
235 static cell
236 parm1(FILE * fbin, char *params, cell opcode)
237 {
238    ucell               p = hex2long(params, NULL);
239
240    if (fbin != NULL)
241      {
242         write_encoded(fbin, (ucell *) & opcode, 1);
243         write_encoded(fbin, &p, 1);
244      }                          /* if */
245    return opcodes(1) + opargs(1);
246 }
247
248 static cell
249 parm2(FILE * fbin, char *params, cell opcode)
250 {
251    ucell               p[2];
252
253    p[0] = hex2long(params, &params);
254    p[1] = hex2long(params, NULL);
255    if (fbin != NULL)
256      {
257         write_encoded(fbin, (ucell *) & opcode, 1);
258         write_encoded(fbin, p, 2);
259      }                          /* if */
260    return opcodes(1) + opargs(2);
261 }
262
263 #if defined __BORLANDC__ || defined __WATCOMC__
264 #pragma argsused
265 #endif
266
267 static cell
268 do_dump(FILE * fbin, char *params, cell opcode __UNUSED__)
269 {
270    ucell               p;
271    int                 num = 0;
272
273    while (*params != '\0')
274      {
275         p = hex2long(params, &params);
276         if (fbin != NULL)
277            write_encoded(fbin, &p, 1);
278         num++;
279         while (isspace(*params))
280            params++;
281      }                          /* while */
282    return num * sizeof(cell);
283 }
284
285 static cell
286 do_call(FILE * fbin, char *params, cell opcode)
287 {
288    char                name[sNAMEMAX + 1];
289    int                 i;
290    symbol             *sym;
291    ucell               p;
292
293    for (i = 0; !isspace(*params); i++, params++)
294      {
295         assert(*params != '\0');
296         assert(i < sNAMEMAX);
297         name[i] = *params;
298      }                          /* for */
299    name[i] = '\0';
300
301    /* look up the function address; note that the correct file number must
302     * already have been set (in order for static globals to be found).
303     */
304    sym = findglb(name);
305    assert(sym != NULL);
306    assert(sym->ident == iFUNCTN || sym->ident == iREFFUNC);
307    assert(sym->vclass == sGLOBAL);
308
309    p = sym->addr;
310    if (fbin != NULL)
311      {
312         write_encoded(fbin, (ucell *) & opcode, 1);
313         write_encoded(fbin, &p, 1);
314      }                          /* if */
315    return opcodes(1) + opargs(1);
316 }
317
318 static cell
319 do_jump(FILE * fbin, char *params, cell opcode)
320 {
321    int                 i;
322    ucell               p;
323
324    i = (int)hex2long(params, NULL);
325    assert(i >= 0 && i < labnum);
326
327    if (fbin != NULL)
328      {
329         assert(lbltab != NULL);
330         p = lbltab[i];
331         write_encoded(fbin, (ucell *) & opcode, 1);
332         write_encoded(fbin, &p, 1);
333      }                          /* if */
334    return opcodes(1) + opargs(1);
335 }
336
337 static cell
338 do_file(FILE * fbin, char *params, cell opcode)
339 {
340    ucell               p, clen;
341    int                 len;
342
343    p = hex2long(params, &params);
344
345    /* remove leading and trailing white space from the filename */
346    while (isspace(*params))
347       params++;
348    len = strlen(params);
349    while (len > 0 && isspace(params[len - 1]))
350       len--;
351    params[len++] = '\0';        /* zero-terminate */
352    while (len % sizeof(cell) != 0)
353       params[len++] = '\0';     /* pad with zeros up to full cell */
354    assert(len > 0 && len < 256);
355    clen = len + sizeof(cell);   /* add size of file ordinal */
356
357    if (fbin != NULL)
358      {
359         write_encoded(fbin, (ucell *) & opcode, 1);
360         write_encoded(fbin, &clen, 1);
361         write_encoded(fbin, &p, 1);
362         write_encoded(fbin, (ucell *) params, len / sizeof(cell));
363      }                          /* if */
364    return opcodes(1) + opargs(1) + clen;        /* other argument is in clen */
365 }
366
367 static cell
368 do_symbol(FILE * fbin, char *params, cell opcode)
369 {
370    char               *endptr;
371    ucell               offset, clen, flags;
372    int                 len;
373    unsigned char       mclass, type;
374
375    for (endptr = params; !isspace(*endptr) && endptr != '\0'; endptr++)
376       /* nothing */ ;
377    assert(*endptr == ' ');
378
379    len = (int)(endptr - params);
380    assert(len > 0 && len < sNAMEMAX);
381    /* first get the other parameters from the line */
382    offset = hex2long(endptr, &endptr);
383    mclass = (unsigned char)hex2long(endptr, &endptr);
384    type = (unsigned char)hex2long(endptr, NULL);
385    flags = type + 256 * mclass;
386    /* now finish up the name (overwriting the input line) */
387    params[len++] = '\0';        /* zero-terminate */
388    while (len % sizeof(cell) != 0)
389       params[len++] = '\0';     /* pad with zeros up to full cell */
390    clen = len + 2 * sizeof(cell);       /* add size of symbol address and flags */
391
392    if (fbin != NULL)
393      {
394         write_encoded(fbin, (ucell *) & opcode, 1);
395         write_encoded(fbin, &clen, 1);
396         write_encoded(fbin, &offset, 1);
397         write_encoded(fbin, &flags, 1);
398         write_encoded(fbin, (ucell *) params, len / sizeof(cell));
399      }                          /* if */
400
401 #if !defined NDEBUG
402    /* function should start right after the symbolic information */
403    if (fbin == NULL && mclass == 0 && type == iFUNCTN)
404       assert(offset == codeindex + opcodes(1) + opargs(1) + clen);
405 #endif
406
407    return opcodes(1) + opargs(1) + clen;        /* other 2 arguments are in clen */
408 }
409
410 static cell
411 do_switch(FILE * fbin, char *params, cell opcode)
412 {
413    int                 i;
414    ucell               p;
415
416    i = (int)hex2long(params, NULL);
417    assert(i >= 0 && i < labnum);
418
419    if (fbin != NULL)
420      {
421         assert(lbltab != NULL);
422         p = lbltab[i];
423         write_encoded(fbin, (ucell *) & opcode, 1);
424         write_encoded(fbin, &p, 1);
425      }                          /* if */
426    return opcodes(1) + opargs(1);
427 }
428
429 #if defined __BORLANDC__ || defined __WATCOMC__
430 #pragma argsused
431 #endif
432
433 static cell
434 do_case(FILE * fbin, char *params, cell opcode __UNUSED__)
435 {
436    int                 i;
437    ucell               p, v;
438
439    v = hex2long(params, &params);
440    i = (int)hex2long(params, NULL);
441    assert(i >= 0 && i < labnum);
442
443    if (fbin != NULL)
444      {
445         assert(lbltab != NULL);
446         p = lbltab[i];
447         write_encoded(fbin, &v, 1);
448         write_encoded(fbin, &p, 1);
449      }                          /* if */
450    return opcodes(0) + opargs(2);
451 }
452
453 #if defined __BORLANDC__ || defined __WATCOMC__
454 #pragma argsused
455 #endif
456
457 static cell
458 curfile(FILE * fbin __UNUSED__, char *params, cell opcode __UNUSED__)
459 {
460    fcurrent = (int)hex2long(params, NULL);
461    return 0;
462 }
463
464 static OPCODE       opcodelist[] = {
465    /* node for "invalid instruction" */
466    {0, NULL, 0, noop},
467    /* opcodes in sorted order */
468    {78, "add", sIN_CSEG, parm0},
469    {87, "add.c", sIN_CSEG, parm1},
470    {14, "addr.alt", sIN_CSEG, parm1},
471    {13, "addr.pri", sIN_CSEG, parm1},
472    {30, "align.alt", sIN_CSEG, parm1},
473    {29, "align.pri", sIN_CSEG, parm1},
474    {81, "and", sIN_CSEG, parm0},
475    {121, "bounds", sIN_CSEG, parm1},
476    {49, "call", sIN_CSEG, do_call},
477    {50, "call.pri", sIN_CSEG, parm0},
478    {0, "case", sIN_CSEG, do_case},
479    {130, "casetbl", sIN_CSEG, parm0},   /* version 1 */
480    {118, "cmps", sIN_CSEG, parm1},
481    {0, "code", 0, noop},
482    {12, "const.alt", sIN_CSEG, parm1},
483    {11, "const.pri", sIN_CSEG, parm1},
484    {0, "curfile", sIN_CSEG, curfile},
485    {0, "data", 0, noop},
486    {114, "dec", sIN_CSEG, parm1},
487    {113, "dec.alt", sIN_CSEG, parm0},
488    {116, "dec.i", sIN_CSEG, parm0},
489    {112, "dec.pri", sIN_CSEG, parm0},
490    {115, "dec.s", sIN_CSEG, parm1},
491    {0, "dump", sIN_DSEG, do_dump},
492    {95, "eq", sIN_CSEG, parm0},
493    {106, "eq.c.alt", sIN_CSEG, parm1},
494    {105, "eq.c.pri", sIN_CSEG, parm1},
495    {124, "file", sIN_CSEG, do_file},
496    {119, "fill", sIN_CSEG, parm1},
497    {100, "geq", sIN_CSEG, parm0},
498    {99, "grtr", sIN_CSEG, parm0},
499    {120, "halt", sIN_CSEG, parm1},
500    {45, "heap", sIN_CSEG, parm1},
501    {27, "idxaddr", sIN_CSEG, parm0},
502    {28, "idxaddr.b", sIN_CSEG, parm1},
503    {109, "inc", sIN_CSEG, parm1},
504    {108, "inc.alt", sIN_CSEG, parm0},
505    {111, "inc.i", sIN_CSEG, parm0},
506    {107, "inc.pri", sIN_CSEG, parm0},
507    {110, "inc.s", sIN_CSEG, parm1},
508    {86, "invert", sIN_CSEG, parm0},
509    {55, "jeq", sIN_CSEG, do_jump},
510    {60, "jgeq", sIN_CSEG, do_jump},
511    {59, "jgrtr", sIN_CSEG, do_jump},
512    {58, "jleq", sIN_CSEG, do_jump},
513    {57, "jless", sIN_CSEG, do_jump},
514    {56, "jneq", sIN_CSEG, do_jump},
515    {54, "jnz", sIN_CSEG, do_jump},
516    {52, "jrel", sIN_CSEG, parm1},       /* always a number */
517    {64, "jsgeq", sIN_CSEG, do_jump},
518    {63, "jsgrtr", sIN_CSEG, do_jump},
519    {62, "jsleq", sIN_CSEG, do_jump},
520    {61, "jsless", sIN_CSEG, do_jump},
521    {51, "jump", sIN_CSEG, do_jump},
522    {128, "jump.pri", sIN_CSEG, parm0},  /* version 1 */
523    {53, "jzer", sIN_CSEG, do_jump},
524    {31, "lctrl", sIN_CSEG, parm1},
525    {98, "leq", sIN_CSEG, parm0},
526    {97, "less", sIN_CSEG, parm0},
527    {25, "lidx", sIN_CSEG, parm0},
528    {26, "lidx.b", sIN_CSEG, parm1},
529    {125, "line", sIN_CSEG, parm2},
530    {2, "load.alt", sIN_CSEG, parm1},
531    {9, "load.i", sIN_CSEG, parm0},
532    {1, "load.pri", sIN_CSEG, parm1},
533    {4, "load.s.alt", sIN_CSEG, parm1},
534    {3, "load.s.pri", sIN_CSEG, parm1},
535    {10, "lodb.i", sIN_CSEG, parm1},
536    {6, "lref.alt", sIN_CSEG, parm1},
537    {5, "lref.pri", sIN_CSEG, parm1},
538    {8, "lref.s.alt", sIN_CSEG, parm1},
539    {7, "lref.s.pri", sIN_CSEG, parm1},
540    {34, "move.alt", sIN_CSEG, parm0},
541    {33, "move.pri", sIN_CSEG, parm0},
542    {117, "movs", sIN_CSEG, parm1},
543    {85, "neg", sIN_CSEG, parm0},
544    {96, "neq", sIN_CSEG, parm0},
545    {134, "nop", sIN_CSEG, parm0},       /* version 6 */
546    {84, "not", sIN_CSEG, parm0},
547    {82, "or", sIN_CSEG, parm0},
548    {43, "pop.alt", sIN_CSEG, parm0},
549    {42, "pop.pri", sIN_CSEG, parm0},
550    {46, "proc", sIN_CSEG, parm0},
551    {40, "push", sIN_CSEG, parm1},
552    {37, "push.alt", sIN_CSEG, parm0},
553    {39, "push.c", sIN_CSEG, parm1},
554    {36, "push.pri", sIN_CSEG, parm0},
555    {38, "push.r", sIN_CSEG, parm1},
556    {41, "push.s", sIN_CSEG, parm1},
557    {133, "pushaddr", sIN_CSEG, parm1},  /* version 4 */
558    {47, "ret", sIN_CSEG, parm0},
559    {48, "retn", sIN_CSEG, parm0},
560    {32, "sctrl", sIN_CSEG, parm1},
561    {73, "sdiv", sIN_CSEG, parm0},
562    {74, "sdiv.alt", sIN_CSEG, parm0},
563    {104, "sgeq", sIN_CSEG, parm0},
564    {103, "sgrtr", sIN_CSEG, parm0},
565    {65, "shl", sIN_CSEG, parm0},
566    {69, "shl.c.alt", sIN_CSEG, parm1},
567    {68, "shl.c.pri", sIN_CSEG, parm1},
568    {66, "shr", sIN_CSEG, parm0},
569    {71, "shr.c.alt", sIN_CSEG, parm1},
570    {70, "shr.c.pri", sIN_CSEG, parm1},
571    {94, "sign.alt", sIN_CSEG, parm0},
572    {93, "sign.pri", sIN_CSEG, parm0},
573    {102, "sleq", sIN_CSEG, parm0},
574    {101, "sless", sIN_CSEG, parm0},
575    {72, "smul", sIN_CSEG, parm0},
576    {88, "smul.c", sIN_CSEG, parm1},
577    {127, "srange", sIN_CSEG, parm2},    /* version 1 */
578    {20, "sref.alt", sIN_CSEG, parm1},
579    {19, "sref.pri", sIN_CSEG, parm1},
580    {22, "sref.s.alt", sIN_CSEG, parm1},
581    {21, "sref.s.pri", sIN_CSEG, parm1},
582    {67, "sshr", sIN_CSEG, parm0},
583    {44, "stack", sIN_CSEG, parm1},
584    {0, "stksize", 0, noop},
585    {16, "stor.alt", sIN_CSEG, parm1},
586    {23, "stor.i", sIN_CSEG, parm0},
587    {15, "stor.pri", sIN_CSEG, parm1},
588    {18, "stor.s.alt", sIN_CSEG, parm1},
589    {17, "stor.s.pri", sIN_CSEG, parm1},
590    {24, "strb.i", sIN_CSEG, parm1},
591    {79, "sub", sIN_CSEG, parm0},
592    {80, "sub.alt", sIN_CSEG, parm0},
593    {132, "swap.alt", sIN_CSEG, parm0},  /* version 4 */
594    {131, "swap.pri", sIN_CSEG, parm0},  /* version 4 */
595    {129, "switch", sIN_CSEG, do_switch},        /* version 1 */
596    {126, "symbol", sIN_CSEG, do_symbol},
597    {136, "symtag", sIN_CSEG, parm1},    /* version 7 */
598    {123, "sysreq.c", sIN_CSEG, parm1},
599    {135, "sysreq.d", sIN_CSEG, parm1},  /* version 7, not generated directly */
600    {122, "sysreq.pri", sIN_CSEG, parm0},
601    {76, "udiv", sIN_CSEG, parm0},
602    {77, "udiv.alt", sIN_CSEG, parm0},
603    {75, "umul", sIN_CSEG, parm0},
604    {35, "xchg", sIN_CSEG, parm0},
605    {83, "xor", sIN_CSEG, parm0},
606    {91, "zero", sIN_CSEG, parm1},
607    {90, "zero.alt", sIN_CSEG, parm0},
608    {89, "zero.pri", sIN_CSEG, parm0},
609    {92, "zero.s", sIN_CSEG, parm1},
610 };
611
612 #define MAX_INSTR_LEN   30
613 static int
614 findopcode(char *instr, int maxlen)
615 {
616    int                 low, high, mid, cmp;
617    char                str[MAX_INSTR_LEN];
618
619    if (maxlen >= MAX_INSTR_LEN)
620       return 0;
621    strncpy(str, instr, maxlen);
622    str[maxlen] = '\0';          /* make sure the string is zero terminated */
623    /* look up the instruction with a binary search
624     * the assembler is case insensitive to instructions (but case sensitive
625     * to symbols)
626     */
627    low = 1;                     /* entry 0 is reserved (for "not found") */
628    high = (sizeof opcodelist / sizeof opcodelist[0]) - 1;
629    while (low < high)
630      {
631         mid = (low + high) / 2;
632         assert(opcodelist[mid].name != NULL);
633         cmp = strcasecmp(str, opcodelist[mid].name);
634         if (cmp > 0)
635            low = mid + 1;
636         else
637            high = mid;
638      }                          /* while */
639
640    assert(low == high);
641    if (strcasecmp(str, opcodelist[low].name) == 0)
642       return low;               /* found */
643    return 0;                    /* not found, return special index */
644 }
645
646 void
647 assemble(FILE * fout, FILE * fin)
648 {
649    typedef struct tagFUNCSTUB
650    {
651       unsigned int            address, nameofs;
652    } FUNCSTUB;
653    AMX_HEADER          hdr;
654    FUNCSTUB            func;
655    int                 numpublics, numnatives, numlibraries, numpubvars,
656       numtags, padding;
657    long                nametablesize, nameofs;
658    char                line[256], *instr, *params;
659    int                 i, pass;
660    short               count;
661    symbol             *sym, **nativelist;
662    constvalue         *constptr;
663    cell                mainaddr;
664    int                 nametable, tags, libraries, publics, natives, pubvars;
665    int                 cod, dat, hea, stp, cip, size, defsize;
666
667 #if !defined NDEBUG
668    /* verify that the opcode list is sorted (skip entry 1; it is reserved
669     * for a non-existant opcode)
670     */
671    assert(opcodelist[1].name != NULL);
672    for (i = 2; i < (sizeof opcodelist / sizeof opcodelist[0]); i++)
673      {
674         assert(opcodelist[i].name != NULL);
675         assert(strcasecmp(opcodelist[i].name, opcodelist[i - 1].name) > 0);
676      }                          /* for */
677 #endif
678
679    writeerror = FALSE;
680    nametablesize = sizeof(short);
681    numpublics = 0;
682    numnatives = 0;
683    numpubvars = 0;
684    mainaddr = -1;
685    /* count number of public and native functions and public variables */
686    for (sym = glbtab.next; sym != NULL; sym = sym->next)
687      {
688         char                alias[sNAMEMAX + 1] = "";
689         int                 match = 0;
690
691         if (sym->ident == iFUNCTN)
692           {
693              assert(strlen(sym->name) <= sNAMEMAX);
694              if ((sym->usage & uNATIVE) != 0 && (sym->usage & uREAD) != 0
695                  && sym->addr >= 0)
696                {
697                   match = ++numnatives;
698                   if (!lookup_alias(alias, sym->name))
699                      strcpy(alias, sym->name);
700                }                /* if */
701              if ((sym->usage & uPUBLIC) != 0 && (sym->usage & uDEFINE) != 0)
702                {
703                   match = ++numpublics;
704                   strcpy(alias, sym->name);
705                }                /* if */
706              if (strcmp(sym->name, uMAINFUNC) == 0)
707                {
708                   assert(sym->vclass == sGLOBAL);
709                   mainaddr = sym->addr;
710                }                /* if */
711           }
712         else if (sym->ident == iVARIABLE)
713           {
714              if ((sym->usage & uPUBLIC) != 0)
715                {
716                   match = ++numpubvars;
717                   strcpy(alias, sym->name);
718                }                /* if */
719           }                     /* if */
720         if (match)
721           {
722              assert(alias[0] != '\0');
723              nametablesize += strlen(alias) + 1;
724           }                     /* if */
725      }                          /* for */
726    assert(numnatives == ntv_funcid);
727
728    /* count number of libraries */
729    numlibraries = 0;
730    for (constptr = libname_tab.next; constptr != NULL;
731         constptr = constptr->next)
732      {
733         if (constptr->value > 0)
734           {
735              assert(constptr->name[0] != '\0');
736              numlibraries++;
737              nametablesize += strlen(constptr->name) + 1;
738           }                     /* if */
739      }                          /* for */
740
741    /* count number of public tags */
742    numtags = 0;
743    for (constptr = tagname_tab.next; constptr != NULL;
744         constptr = constptr->next)
745      {
746         if ((constptr->value & PUBLICTAG) != 0)
747           {
748              assert(constptr->name[0] != '\0');
749              numtags++;
750              nametablesize += strlen(constptr->name) + 1;
751           }                     /* if */
752      }                          /* for */
753
754    /* pad the header to sc_dataalign
755     * => thereby the code segment is aligned
756     * => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned
757     * => and thereby the stack top is aligned too
758     */
759    assert(sc_dataalign != 0);
760    padding = sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign;
761    if (padding == sc_dataalign)
762       padding = 0;
763
764    /* write the abstract machine header */
765    memset(&hdr, 0, sizeof hdr);
766    hdr.magic = (unsigned short)0xF1E0;
767    hdr.file_version = CUR_FILE_VERSION;
768    hdr.amx_version = MIN_AMX_VERSION;
769    hdr.flags = (short)(sc_debug & sSYMBOLIC);
770    if (charbits == 16)
771       hdr.flags |= AMX_FLAG_CHAR16;
772    if (sc_compress)
773       hdr.flags |= AMX_FLAG_COMPACT;
774    if (sc_debug == 0)
775       hdr.flags |= AMX_FLAG_NOCHECKS;
776 //  #ifdef WORDS_BIGENDIAN
777 //    hdr.flags|=AMX_FLAG_BIGENDIAN;
778 //  #endif
779    defsize = hdr.defsize = sizeof(FUNCSTUB);
780    assert((hdr.defsize % sizeof(cell)) == 0);
781    publics = hdr.publics = sizeof hdr;  /* public table starts right after the header */
782    natives = hdr.natives = hdr.publics + numpublics * sizeof(FUNCSTUB);
783    libraries = hdr.libraries = hdr.natives + numnatives * sizeof(FUNCSTUB);
784    pubvars = hdr.pubvars = hdr.libraries + numlibraries * sizeof(FUNCSTUB);
785    tags = hdr.tags = hdr.pubvars + numpubvars * sizeof(FUNCSTUB);
786    nametable = hdr.nametable = hdr.tags + numtags * sizeof(FUNCSTUB);
787    cod = hdr.cod = hdr.nametable + nametablesize + padding;
788    dat = hdr.dat = hdr.cod + code_idx;
789    hea = hdr.hea = hdr.dat + glb_declared * sizeof(cell);
790    stp = hdr.stp = hdr.hea + sc_stksize * sizeof(cell);
791    cip = hdr.cip = mainaddr;
792    size = hdr.size = hdr.hea;   /* preset, this is incorrect in case of compressed output */
793 #ifdef WORDS_BIGENDIAN
794    align32(&hdr.size);
795    align16(&hdr.magic);
796    align16(&hdr.flags);
797    align16(&hdr.defsize);
798    align32(&hdr.cod);
799    align32(&hdr.dat);
800    align32(&hdr.hea);
801    align32(&hdr.stp);
802    align32(&hdr.cip);
803    align32(&hdr.publics);
804    align32(&hdr.natives);
805    align32(&hdr.libraries);
806    align32(&hdr.pubvars);
807    align32(&hdr.tags);
808    align32(&hdr.nametable);
809 #endif
810    sc_writebin(fout, &hdr, sizeof hdr);
811
812    /* dump zeros up to the rest of the header, so that we can easily "seek" */
813    for (nameofs = sizeof hdr; nameofs < cod; nameofs++)
814       putc(0, fout);
815    nameofs = nametable + sizeof(short);
816
817    /* write the public functions table */
818    count = 0;
819    for (sym = glbtab.next; sym != NULL; sym = sym->next)
820      {
821         if (sym->ident == iFUNCTN
822             && (sym->usage & uPUBLIC) != 0 && (sym->usage & uDEFINE) != 0)
823           {
824              assert(sym->vclass == sGLOBAL);
825              func.address = sym->addr;
826              func.nameofs = nameofs;
827 #ifdef WORDS_BIGENDIAN
828              align32(&func.address);
829              align32(&func.nameofs);
830 #endif
831              fseek(fout, publics + count * sizeof(FUNCSTUB), SEEK_SET);
832              sc_writebin(fout, &func, sizeof func);
833              fseek(fout, nameofs, SEEK_SET);
834              sc_writebin(fout, sym->name, strlen(sym->name) + 1);
835              nameofs += strlen(sym->name) + 1;
836              count++;
837           }                     /* if */
838      }                          /* for */
839
840    /* write the natives table */
841    /* The native functions must be written in sorted order. (They are
842     * sorted on their "id", not on their name). A nested loop to find
843     * each successive function would be an O(n^2) operation. But we
844     * do not really need to sort, because the native function id's
845     * are sequential and there are no duplicates. So we first walk
846     * through the complete symbol list and store a pointer to every
847     * native function of interest in a temporary table, where its id
848     * serves as the index in the table. Now we can walk the table and
849     * have all native functions in sorted order.
850     */
851    if (numnatives > 0)
852      {
853         nativelist = (symbol **) malloc(numnatives * sizeof(symbol *));
854         if (nativelist == NULL)
855            error(103);          /* insufficient memory */
856 #if !defined NDEBUG
857         memset(nativelist, 0, numnatives * sizeof(symbol *));   /* for NULL checking */
858 #endif
859         for (sym = glbtab.next; sym != NULL; sym = sym->next)
860           {
861              if (sym->ident == iFUNCTN && (sym->usage & uNATIVE) != 0
862                  && (sym->usage & uREAD) != 0 && sym->addr >= 0)
863                {
864                   assert(sym->addr < numnatives);
865                   nativelist[(int)sym->addr] = sym;
866                }                /* if */
867           }                     /* for */
868         count = 0;
869         for (i = 0; i < numnatives; i++)
870           {
871              char                alias[sNAMEMAX + 1];
872
873              sym = nativelist[i];
874              assert(sym != NULL);
875              if (!lookup_alias(alias, sym->name))
876                {
877                   assert(strlen(sym->name) <= sNAMEMAX);
878                   strcpy(alias, sym->name);
879                }                /* if */
880              assert(sym->vclass == sGLOBAL);
881              func.address = 0;
882              func.nameofs = nameofs;
883 #ifdef WORDS_BIGENDIAN
884              align32(&func.address);
885              align32(&func.nameofs);
886 #endif
887              fseek(fout, natives + count * sizeof(FUNCSTUB), SEEK_SET);
888              sc_writebin(fout, &func, sizeof func);
889              fseek(fout, nameofs, SEEK_SET);
890              sc_writebin(fout, alias, strlen(alias) + 1);
891              nameofs += strlen(alias) + 1;
892              count++;
893           }                     /* for */
894         free(nativelist);
895      }                          /* if */
896
897    /* write the libraries table */
898    count = 0;
899    for (constptr = libname_tab.next; constptr != NULL;
900         constptr = constptr->next)
901      {
902         if (constptr->value > 0)
903           {
904              assert(constptr->name[0] != '\0');
905              func.address = 0;
906              func.nameofs = nameofs;
907 #ifdef WORDS_BIGENDIAN
908              align32(&func.address);
909              align32(&func.nameofs);
910 #endif
911              fseek(fout, libraries + count * sizeof(FUNCSTUB), SEEK_SET);
912              sc_writebin(fout, &func, sizeof func);
913              fseek(fout, nameofs, SEEK_SET);
914              sc_writebin(fout, constptr->name, strlen(constptr->name) + 1);
915              nameofs += strlen(constptr->name) + 1;
916              count++;
917           }                     /* if */
918      }                          /* for */
919
920    /* write the public variables table */
921    count = 0;
922    for (sym = glbtab.next; sym != NULL; sym = sym->next)
923      {
924         if (sym->ident == iVARIABLE && (sym->usage & uPUBLIC) != 0)
925           {
926              assert((sym->usage & uDEFINE) != 0);
927              assert(sym->vclass == sGLOBAL);
928              func.address = sym->addr;
929              func.nameofs = nameofs;
930 #ifdef WORDS_BIGENDIAN
931              align32(&func.address);
932              align32(&func.nameofs);
933 #endif
934              fseek(fout, pubvars + count * sizeof(FUNCSTUB), SEEK_SET);
935              sc_writebin(fout, &func, sizeof func);
936              fseek(fout, nameofs, SEEK_SET);
937              sc_writebin(fout, sym->name, strlen(sym->name) + 1);
938              nameofs += strlen(sym->name) + 1;
939              count++;
940           }                     /* if */
941      }                          /* for */
942
943    /* write the public tagnames table */
944    count = 0;
945    for (constptr = tagname_tab.next; constptr != NULL;
946         constptr = constptr->next)
947      {
948         if ((constptr->value & PUBLICTAG) != 0)
949           {
950              assert(constptr->name[0] != '\0');
951              func.address = constptr->value & TAGMASK;
952              func.nameofs = nameofs;
953 #ifdef WORDS_BIGENDIAN
954              align32(&func.address);
955              align32(&func.nameofs);
956 #endif
957              fseek(fout, tags + count * sizeof(FUNCSTUB), SEEK_SET);
958              sc_writebin(fout, &func, sizeof func);
959              fseek(fout, nameofs, SEEK_SET);
960              sc_writebin(fout, constptr->name, strlen(constptr->name) + 1);
961              nameofs += strlen(constptr->name) + 1;
962              count++;
963           }                     /* if */
964      }                          /* for */
965
966    /* write the "maximum name length" field in the name table */
967    assert(nameofs == nametable + nametablesize);
968    fseek(fout, nametable, SEEK_SET);
969    count = sNAMEMAX;
970 #ifdef WORDS_BIGENDIAN
971    align16(&count);
972 #endif
973    sc_writebin(fout, &count, sizeof count);
974    fseek(fout, cod, SEEK_SET);
975
976    /* First pass: relocate all labels */
977    /* This pass is necessary because the code addresses of labels is only known
978     * after the peephole optimization flag. Labels can occur inside expressions
979     * (e.g. the conditional operator), which are optimized.
980     */
981    lbltab = NULL;
982    if (labnum > 0)
983      {
984         /* only very short programs have zero labels; no first pass is needed
985          * if there are no labels */
986         lbltab = (cell *) malloc(labnum * sizeof(cell));
987         if (lbltab == NULL)
988            error(103);          /* insufficient memory */
989         codeindex = 0;
990         sc_resetasm(fin);
991         while (sc_readasm(fin, line, sizeof line) != NULL)
992           {
993              stripcomment(line);
994              instr = skipwhitespace(line);
995              /* ignore empty lines */
996              if (*instr == '\0')
997                 continue;
998              if (tolower(*instr) == 'l' && *(instr + 1) == '.')
999                {
1000                   int                 lindex = (int)hex2long(instr + 2, NULL);
1001
1002                   assert(lindex < labnum);
1003                   lbltab[lindex] = codeindex;
1004                }
1005              else
1006                {
1007                   /* get to the end of the instruction (make use of the '\n' that fgets()
1008                    * added at the end of the line; this way we will *always* drop on a
1009                    * whitespace character) */
1010                   for (params = instr; *params != '\0' && !isspace(*params);
1011                        params++)
1012                      /* nothing */ ;
1013                   assert(params > instr);
1014                   i = findopcode(instr, (int)(params - instr));
1015                   if (opcodelist[i].name == NULL)
1016                     {
1017                        *params = '\0';
1018                        error(104, instr);       /* invalid assembler instruction */
1019                     }           /* if */
1020                   if (opcodelist[i].segment == sIN_CSEG)
1021                      codeindex +=
1022                         opcodelist[i].func(NULL, skipwhitespace(params),
1023                                            opcodelist[i].opcode);
1024                }                /* if */
1025           }                     /* while */
1026      }                          /* if */
1027
1028    /* Second pass (actually 2 more passes, one for all code and one for all data) */
1029    bytes_in = 0;
1030    bytes_out = 0;
1031    for (pass = sIN_CSEG; pass <= sIN_DSEG; pass++)
1032      {
1033         sc_resetasm(fin);
1034         while (sc_readasm(fin, line, sizeof line) != NULL)
1035           {
1036              stripcomment(line);
1037              instr = skipwhitespace(line);
1038              /* ignore empty lines and labels (labels have a special syntax, so these
1039               * must be parsed separately) */
1040              if (*instr == '\0' || (tolower(*instr) == 'l'
1041                  && *(instr + 1) == '.'))
1042                 continue;
1043              /* get to the end of the instruction (make use of the '\n' that fgets()
1044               * added at the end of the line; this way we will *always* drop on a
1045               * whitespace character) */
1046              for (params = instr; *params != '\0' && !isspace(*params);
1047                   params++)
1048                 /* nothing */ ;
1049              assert(params > instr);
1050              i = findopcode(instr, (int)(params - instr));
1051              assert(opcodelist[i].name != NULL);
1052              if (opcodelist[i].segment == pass)
1053                 opcodelist[i].func(fout, skipwhitespace(params),
1054                                    opcodelist[i].opcode);
1055           }                     /* while */
1056      }                          /* for */
1057    if (bytes_out - bytes_in > 0)
1058       error(106);               /* compression buffer overflow */
1059
1060    if (lbltab != NULL)
1061      {
1062         free(lbltab);
1063 #if !defined NDEBUG
1064         lbltab = NULL;
1065 #endif
1066      }                          /* if */
1067
1068    if (writeerror)
1069       error(101, "disk full");
1070
1071    /* adjust the header */
1072    if (sc_compress)
1073      {
1074         hdr.size = sc_lengthbin(fout);
1075 #ifdef WORDS_BIGENDIAN
1076         align32(&hdr.size);
1077 #endif
1078         sc_resetbin(fout);      /* "size" is the very first field */
1079         sc_writebin(fout, &hdr.size, sizeof hdr.size);
1080      }                          /* if */
1081 }