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