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