]> git.xonotic.org Git - xonotic/gmqcc.git/blob - assembler.c
tabulators->four spaces
[xonotic/gmqcc.git] / assembler.c
1 /*
2  * Copyright (C) 2012 
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include "gmqcc.h"
24 /*
25  * This is the assembler, gmqas, this is being implemented because I'm
26  * not exactly sure how codegen would work for the C compiler as of yet
27  * and also I plan to allow inline assembly for the compiler.
28  */
29 static const struct {
30     const char  *m; /* menomic     */
31     const size_t o; /* operands    */ 
32     const size_t l; /* menomic len */
33 } const asm_instr[] = {
34     [INSTR_DONE]       = { "DONE"      , 1, 4 },
35     [INSTR_MUL_F]      = { "MUL_F"     , 3, 5 },
36     [INSTR_MUL_V]      = { "MUL_V"     , 3, 5 },
37     [INSTR_MUL_FV]     = { "MUL_FV"    , 3, 6 },
38     [INSTR_MUL_VF]     = { "MUL_VF"    , 3, 6 },
39     [INSTR_DIV_F]      = { "DIV"       , 0, 3 },
40     [INSTR_ADD_F]      = { "ADD_F"     , 3, 5 },
41     [INSTR_ADD_V]      = { "ADD_V"     , 3, 5 },
42     [INSTR_SUB_F]      = { "SUB_F"     , 3, 5 },
43     [INSTR_SUB_V]      = { "DUB_V"     , 3, 5 },
44     [INSTR_EQ_F]       = { "EQ_F"      , 0, 4 },
45     [INSTR_EQ_V]       = { "EQ_V"      , 0, 4 },
46     [INSTR_EQ_S]       = { "EQ_S"      , 0, 4 },
47     [INSTR_EQ_E]       = { "EQ_E"      , 0, 4 },
48     [INSTR_EQ_FNC]     = { "ES_FNC"    , 0, 6 },
49     [INSTR_NE_F]       = { "NE_F"      , 0, 4 },
50     [INSTR_NE_V]       = { "NE_V"      , 0, 4 },
51     [INSTR_NE_S]       = { "NE_S"      , 0, 4 },
52     [INSTR_NE_E]       = { "NE_E"      , 0, 4 },
53     [INSTR_NE_FNC]     = { "NE_FNC"    , 0, 6 },
54     [INSTR_LE]         = { "LE"        , 0, 2 },
55     [INSTR_GE]         = { "GE"        , 0, 2 },
56     [INSTR_LT]         = { "LT"        , 0, 2 },
57     [INSTR_GT]         = { "GT"        , 0, 2 },
58     [INSTR_LOAD_F]     = { "FIELD_F"   , 0, 7 },
59     [INSTR_LOAD_V]     = { "FIELD_V"   , 0, 7 },
60     [INSTR_LOAD_S]     = { "FIELD_S"   , 0, 7 },
61     [INSTR_LOAD_ENT]   = { "FIELD_ENT" , 0, 9 },
62     [INSTR_LOAD_FLD]   = { "FIELD_FLD" , 0, 9 },
63     [INSTR_LOAD_FNC]   = { "FIELD_FNC" , 0, 9 },
64     [INSTR_ADDRESS]    = { "ADDRESS"   , 0, 7 },
65     [INSTR_STORE_F]    = { "STORE_F"   , 0, 7 },
66     [INSTR_STORE_V]    = { "STORE_V"   , 0, 7 },
67     [INSTR_STORE_S]    = { "STORE_S"   , 0, 7 },
68     [INSTR_STORE_ENT]  = { "STORE_ENT" , 0, 9 },
69     [INSTR_STORE_FLD]  = { "STORE_FLD" , 0, 9 },
70     [INSTR_STORE_FNC]  = { "STORE_FNC" , 0, 9 },
71     [INSTR_STOREP_F]   = { "STOREP_F"  , 0, 8 },
72     [INSTR_STOREP_V]   = { "STOREP_V"  , 0, 8 },
73     [INSTR_STOREP_S]   = { "STOREP_S"  , 0, 8 },
74     [INSTR_STOREP_ENT] = { "STOREP_ENT", 0, 10},
75     [INSTR_STOREP_FLD] = { "STOREP_FLD", 0, 10},
76     [INSTR_STOREP_FNC] = { "STOREP_FNC", 0, 10},
77     [INSTR_RETURN]     = { "RETURN"    , 0, 6 },
78     [INSTR_NOT_F]      = { "NOT_F"     , 0, 5 },
79     [INSTR_NOT_V]      = { "NOT_V"     , 0, 5 },
80     [INSTR_NOT_S]      = { "NOT_S"     , 0, 5 },
81     [INSTR_NOT_ENT]    = { "NOT_ENT"   , 0, 7 },
82     [INSTR_NOT_FNC]    = { "NOT_FNC"   , 0, 7 },
83     [INSTR_IF]         = { "IF"        , 0, 2 },
84     [INSTR_IFNOT]      = { "IFNOT"     , 0, 5 },
85     [INSTR_CALL0]      = { "CALL0"     , 0, 5 },
86     [INSTR_CALL1]      = { "CALL1"     , 0, 5 },
87     [INSTR_CALL2]      = { "CALL2"     , 0, 5 },
88     [INSTR_CALL3]      = { "CALL3"     , 0, 5 },
89     [INSTR_CALL4]      = { "CALL4"     , 0, 5 },
90     [INSTR_CALL5]      = { "CALL5"     , 0, 5 },
91     [INSTR_CALL6]      = { "CALL6"     , 0, 5 },
92     [INSTR_CALL7]      = { "CALL7"     , 0, 5 },
93     [INSTR_CALL8]      = { "CALL8"     , 0, 5 },
94     [INSTR_STATE]      = { "STATE"     , 0, 5 },
95     [INSTR_GOTO]       = { "GOTO"      , 0, 4 },
96     [INSTR_AND]        = { "AND"       , 0, 3 },
97     [INSTR_OR]         = { "OR"        , 0, 2 },
98     [INSTR_BITAND]     = { "BITAND"    , 0, 6 },
99     [INSTR_BITOR]      = { "BITOR"     , 0, 5 }
100 };
101
102 /*
103  * Some assembler keywords not part of the opcodes above: these are
104  * for creating functions, or constants.
105  */
106 const char *const asm_keys[] = {
107     "FLOAT"    , /* define float  */
108     "VECTOR"   , /* define vector */
109     "ENTITY"   , /* define ent    */
110     "FIELD"    , /* define field  */
111     "STRING"   , /* define string */
112     "FUNCTION"
113 };
114
115 static char *const asm_getline(size_t *byte, FILE *fp) {
116     char   *line = NULL;
117     ssize_t read = util_getline(&line, byte, fp);
118     *byte = read;
119     if (read == -1) {
120         mem_d (line);
121         return NULL;
122     }
123     return line;
124 }
125
126 #define asm_rmnewline(L,S) *((L)+*(S)-1) = '\0'
127 #define asm_skipwhite(L)             \
128     while((*(L)==' '||*(L)=='\t')) { \
129         (L)++;                       \
130     }
131     
132 void asm_init(const char *file, FILE **fp) {
133     *fp = fopen(file, "r");
134     code_init();
135 }
136
137 void asm_close(FILE *fp) {
138     fclose(fp);
139     code_write();
140 }
141
142 /*
143  * Following parse states:
144  *     ASM_FUNCTION -- in a function accepting input statements
145  *     ....
146  */
147 typedef enum {
148     ASM_NULL,
149     ASM_FUNCTION
150 } asm_state;
151
152 typedef struct {
153     char *name;   /* name of constant    */
154     int   offset; /* location in globals */
155 } globals;
156 VECTOR_MAKE(globals, assembly_constants);
157     
158 void asm_parse(FILE *fp) {
159     char     *data  = NULL;
160     char     *skip  = NULL;
161     long      line  = 1; /* current line */
162     size_t    size  = 0; /* size of line */
163     asm_state state = ASM_NULL;
164     
165     while ((data = asm_getline(&size, fp)) != NULL) {
166         skip = data;
167         asm_skipwhite(skip);
168         asm_rmnewline(skip, &size);
169         
170         #define DECLTYPE(X, CODE)                                         \
171             if (!strncmp(X, skip, strlen(X))) {                           \
172                 if (skip[strlen(X)] != ':') {                             \
173                     printf("%li: Missing `:` after decltype\n",line);     \
174                     exit (1);                                             \
175                 }                                                         \
176                 skip += strlen(X)+1;                                      \
177                 asm_skipwhite(skip);                                      \
178                 if(!isalpha(*skip)) {                                     \
179                     printf("%li: Invalid identififer: %s\n", line, skip); \
180                     exit (1);                                             \
181                 } else {                                                  \
182                     size_t offset_code      = code_statements_elements+1; \
183                     size_t offset_chars     = code_strings_elements   +1; \
184                     size_t offset_globals   = code_globals_elements   +1; \
185                     size_t offset_functions = code_functions_elements +1; \
186                     size_t offset_fields    = code_fields_elements    +1; \
187                     size_t offset_defs      = code_defs_elements      +1; \
188                     CODE                                                  \
189                     /* silent unused warnings */                          \
190                     (void)offset_code;                                    \
191                     (void)offset_chars;                                   \
192                     (void)offset_globals;                                 \
193                     (void)offset_functions;                               \
194                     (void)offset_fields;                                  \
195                     (void)offset_defs;                                    \
196                     assembly_constants_add((globals){                     \
197                         .name   = util_strdup(skip),                      \
198                         .offset = offset_globals                          \
199                     });                                                   \
200                 }                                                         \
201                 goto end;                                                 \
202             }
203         
204         /* FLOAT    */
205         DECLTYPE(asm_keys[0], {
206             code_defs_add((prog_section_def){
207                 .type   = TYPE_FLOAT,
208                 .offset = offset_globals, /* global table */
209                 .name   = offset_chars    /* string table TODO */
210             });
211             float f = 0; /*TODO*/
212             code_globals_add(*(int*)&f);
213             
214         });
215         DECLTYPE(asm_keys[1], {
216             code_defs_add((prog_section_def){
217                 .type   = TYPE_FLOAT,
218                 .offset = offset_globals, /* global table */
219                 .name   = offset_chars    /* string table TODO */
220             });
221             float f1 = 0;
222             float f2 = 0;
223             float f3 = 0;
224             code_globals_add(*(int*)&f1);
225             code_globals_add(*(int*)&f2);
226             code_globals_add(*(int*)&f3);
227         });
228         /* ENTITY   */ DECLTYPE(asm_keys[2], {});
229         /* FIELD    */ DECLTYPE(asm_keys[3], {});
230         /* STRING   */
231         DECLTYPE(asm_keys[4], {
232             code_defs_add((prog_section_def){
233                 .type   = TYPE_STRING,    
234                 .offset = offset_globals, /* offset to offset in string table (for data)*/
235                 .name   = offset_chars    /* location of name in string table (for name)*/
236             });
237             code_strings_add('h');
238         });
239         /* FUNCTION */
240         DECLTYPE(asm_keys[5], {
241             /* TODO: parse */
242             if (state != ASM_NULL) {
243                 printf("%li: Error unfinished function block, expected DONE or RETURN\n", line);
244                 goto end;
245             }
246             
247             state = ASM_FUNCTION;
248             code_defs_add((prog_section_def){
249                 .type   = TYPE_VOID,
250                 .offset = offset_globals,
251                 .name   = offset_chars
252             });
253             code_globals_add(offset_functions);
254             code_functions_add((prog_section_function){
255                 .entry      =  offset_code,      
256                 .firstlocal =  0,
257                 .locals     =  0,
258                 .profile    =  0,
259                 .name       =  offset_chars,
260                 .file       =  0,
261                 .nargs      =  0,
262                 .argsize    = {0}
263             });
264         });
265         
266         /* if we make it this far then we have statements */
267         {
268             size_t i = 0;    /* counter   */
269             size_t o = 0;    /* operands  */
270             char  *t = NULL; /* token     */
271             
272             /*
273              * Most ops a single statement can have is three.
274              * lets allocate some space for all of those here.
275              */
276             char op[3][32768] = {{0},{0},{0}};
277             for (; i < sizeof(asm_instr)/sizeof(*asm_instr); i++) {
278                 if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) {
279                     if (state != ASM_FUNCTION) {
280                         printf("%li: Statement not inside function block\n", line);
281                         goto end;
282                     }
283                     
284                     /* update parser state */
285                     if (i == INSTR_DONE || i == INSTR_RETURN) {
286                         goto end;
287                         state = ASM_NULL;
288                     }
289                     
290                     /* parse the statement */
291                     o     = asm_instr[i].o; /* operands         */
292                     skip += asm_instr[i].l; /* skip instruction */
293                     t     = strtok(skip, " ,");
294                     i     = 0;
295                     while (t != NULL && i < 3) {
296                         strcpy(op[i], t);
297                         t = strtok(NULL, " ,");
298                         i ++;
299                     }
300                     
301                     util_debug("ASM", "Operand 1: %s\n", util_strrnl(op[0]));
302                     util_debug("ASM", "Operand 2: %s\n", util_strrnl(op[1]));
303                     util_debug("ASM", "Operand 3: %s\n", util_strrnl(op[2]));
304                     
305                     /* check */
306                     if (i != o) {
307                         printf("not enough operands, expected: %li, got %li\n", o, i);
308                     }
309                     
310                     /* TODO: hashtable value LOAD .... etc */
311                     code_statements_add((prog_section_statement){
312                         i, {0}, {0}, {0}
313                     });
314                     goto end;
315                 }
316             }
317         }
318         
319         /* if we made it this far something is wrong */
320         if (*skip != '\0')
321             printf("%li: Invalid statement, expression, or decleration\n", line);
322         
323         end:
324         //free(data);
325         mem_d(data);
326         line ++;
327     }
328 }