X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=asm.c;h=c73f25cf7956b4121a6f5e2c6c6088ef8a2991db;hb=91e3765a000a1fab15232e97538442b6fe5024cc;hp=24b9e0dcb24de16a6e6812bccbd55929cdb62f6b;hpb=629f6f1e634a6bdd1f3494505d0c4173c293fadf;p=xonotic%2Fgmqcc.git diff --git a/asm.c b/asm.c index 24b9e0d..c73f25c 100644 --- a/asm.c +++ b/asm.c @@ -32,18 +32,19 @@ typedef enum { } asm_state; typedef struct { - char *name; /* name of constant */ - char type; /* type, float, vector, string */ + char *name; + char type; /* type, float, vector, string, function*/ char elem; /* 0=x, 1=y, or 2=Z? */ int offset; /* location in globals */ -} globals; -VECTOR_MAKE(globals, assembly_constants); + bool isconst; +} asm_sym; +VECTOR_MAKE(asm_sym, asm_symbols); /* * Assembly text processing: this handles the internal collection * of text to allow parsing and assemblation. */ -static char *const asm_getline(size_t *byte, FILE *fp) { +static char* asm_getline(size_t *byte, FILE *fp) { char *line = NULL; size_t read = util_getline(&line, byte, fp); *byte = read; @@ -64,13 +65,13 @@ void asm_init(const char *file, FILE **fp) { } void asm_close(FILE *fp) { fclose(fp); - code_write(); + code_write("program.dat"); } void asm_clear() { size_t i = 0; - for (; i < assembly_constants_elements; i++) - mem_d(assembly_constants_data[i].name); - mem_d(assembly_constants_data); + for (; i < asm_symbols_elements; i++) + mem_d(asm_symbols_data[i].name); + mem_d(asm_symbols_data); } /* @@ -79,8 +80,9 @@ void asm_clear() { */ void asm_dumps() { size_t i = 0; - for (; i < assembly_constants_elements; i++) { - globals *g = &assembly_constants_data[i]; + for (; i < asm_symbols_elements; i++) { + asm_sym *g = &asm_symbols_data[i]; + if (!g->isconst) continue; switch (g->type) { case TYPE_VECTOR: { util_debug("ASM", "vector %s %c[%f]\n", g->name, @@ -91,6 +93,9 @@ void asm_dumps() { ); break; } + case TYPE_FUNCTION: { + util_debug("ASM", "function %s\n", g->name); + } } } } @@ -102,11 +107,11 @@ void asm_dumps() { * are locals. */ static GMQCC_INLINE bool asm_parse_type(const char *skip, size_t line, asm_state *state) { - if (!(strstr(skip, "FLOAT:") == &skip[0]) && - (strstr(skip, "VECTOR:") == &skip[0]) && - (strstr(skip, "ENTITY:") == &skip[0]) && - (strstr(skip, "FIELD:") == &skip[0]) && - (strstr(skip, "STRING:") == &skip[0])) return false; + if ((strstr(skip, "FLOAT:") != &skip[0]) && + (strstr(skip, "VECTOR:") != &skip[0]) && + (strstr(skip, "ENTITY:") != &skip[0]) && + (strstr(skip, "FIELD:") != &skip[0]) && + (strstr(skip, "STRING:") != &skip[0])) return false; /* TODO: determine if constant, global, or local */ switch (*skip) { @@ -114,7 +119,7 @@ static GMQCC_INLINE bool asm_parse_type(const char *skip, size_t line, asm_state float val1; float val2; float val3; - globals global; + asm_sym sym; char *find = (char*)skip + 7; char *name = (char*)skip + 7; @@ -150,12 +155,12 @@ static GMQCC_INLINE bool asm_parse_type(const char *skip, size_t line, asm_state PARSE_ELEMENT(find, val2, { find ++; while (*find == ' ') { find ++; } }); PARSE_ELEMENT(find, val3, { find ++; /* no need to do anything here */ }); #undef PARSE_ELEMENT - #define BUILD_ELEMENT(X,Y) \ - global.type = TYPE_VECTOR; \ - global.name = util_strdup(name); \ - global.elem = (X); \ - global.offset = code_globals_elements; \ - assembly_constants_add(global); \ + #define BUILD_ELEMENT(X,Y) \ + sym.type = TYPE_VECTOR; \ + sym.name = util_strdup(name); \ + sym.elem = (X); \ + sym.offset = code_globals_elements; \ + asm_symbols_add(sym); \ code_globals_add(FLT2INT(Y)) BUILD_ELEMENT(0, val1); BUILD_ELEMENT(1, val2); @@ -190,12 +195,23 @@ static GMQCC_INLINE bool asm_parse_type(const char *skip, size_t line, asm_state * internal engine function selection. */ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state *state) { - if (*state == ASM_FUNCTION && (strstr(skip, "FUNCTION:") == &skip[0])) + if (*state == ASM_FUNCTION) return false; if (strstr(skip, "FUNCTION:") == &skip[0]) { - char *copy = util_strsws(skip+10); - char *name = util_strchp(copy, strchr(copy, '\0')); + asm_sym sym; + char *look = util_strdup(skip+10); + char *copy = look; + char *name = NULL; + while (*copy == ' ' || *copy == '\t') copy++; + + memset(&sym, 0, sizeof(asm_sym)); + + /* + * Chop the function name out of the string, this allocates + * a new string. + */ + name = util_strchp(copy, strchr(copy, '\0')); /* TODO: failure system, missing name */ if (!name) { @@ -217,10 +233,11 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state * to determine this. */ if (strchr(name, ',')) { + char *find = strchr(name, ',') + 1; prog_section_function function; prog_section_def def; - - char *find = strchr(name, ',') + 1; + memset(&function, 0, sizeof(prog_section_function)); + memset(&def, 0, sizeof(prog_section_def)); /* skip whitespace */ while (*find == ' ' || *find == '\t') @@ -250,6 +267,7 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state */ function.entry = -atoi(find); function.firstlocal = 0; + function.locals = 0; function.profile = 0; function.name = code_chars_elements; function.file = 0; @@ -259,10 +277,13 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state def.name = code_chars_elements; code_functions_add(function); code_defs_add (def); - code_globals_add (code_chars_elements); code_chars_put (name, strlen(name)); code_chars_add ('\0'); - + sym.type = TYPE_FUNCTION; + sym.name = util_strdup(name); + sym.offset = function.entry; + asm_symbols_add(sym); + util_debug("ASM", "added internal function %s to function table\n", name); /* @@ -272,7 +293,7 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state * produce a valid constant that would lead to runtime problems. */ if (util_strdigit(find)) - printf("found internal function %s, -%d\n", name, atoi(find)); + util_debug("ASM", "found internal function %s, -%d\n", name, atoi(find)); else printf("invalid internal function identifier, must be all numeric\n"); @@ -283,15 +304,18 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state * the `#` (pound sign). */ int args = 0; + int size = 0; char *find = strchr(name, '#'); char *peek = find; - + /* * Code structures for filling after determining the correct * information to add to the code write system. */ prog_section_function function; prog_section_def def; + memset(&function, 0, sizeof(prog_section_function)); + memset(&def, 0, sizeof(prog_section_def)); if (find) { find ++; @@ -304,7 +328,7 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state * invalid and shouldn't be allowed. The QuakeC VM only * allows a maximum of eight arguments. */ - if (strlen(find) > 1 || *find == '9') { + if (*find == '9') { printf("invalid number of arguments, must be a valid number from 0-8\n"); mem_d(copy); mem_d(name); @@ -324,11 +348,55 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state case '2': args++; case '1': args++; } } + /* + * We need to parse the argument size now by determining + * the argument identifer list used after the amount of + * arguments. + */ + memset(function.argsize, 0, sizeof(function.argsize)); + find ++; /* skip the number */ + while (*find == ' ' || *find == '\t') find++; + while (size < args) { + switch (*find) { + case 'V': case 'v': function.argsize[size]=3; break; + case 'S': case 's': + case 'F': case 'f': + case 'E': case 'e': function.argsize[size]=1; break; + case '\0': + printf("missing argument identifer, expected %d\n", args); + return false; + default: + printf("error invalid function argument identifier\n"); + return false; + } + size++,find++; + } + while (*find == ' ' || *find == '\t') find++; + if (*find != '\0') { + printf("too many function argument identifers expected %d\n", args); + return false; + } } else { printf("missing number of argument count in function %s\n", name); + return false; } - /* terminate name inspot */ - *--peek='\0'; + + /* + * Now we need to strip the name apart into it's exact size + * by working in the peek buffer till we hit the name again. + */ + if (*peek == '#') { + peek --; /* '#' */ + peek --; /* number */ + } + while (*peek == ' ' || *peek == '\t') peek--; + + /* + * We're guranteed to be exactly where we need to be in the + * peek buffer to null terminate and get our name from name + * without any garbage before or after it. + */ + *++peek='\0'; /* * We got valid function structure information now. Lets add @@ -336,6 +404,7 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state */ function.entry = code_statements_elements; function.firstlocal = 0; + function.locals = 0; function.profile = 0; function.name = code_chars_elements; function.file = 0; @@ -344,16 +413,20 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state def.offset = code_globals_elements; def.name = code_chars_elements; code_functions_add(function); - code_defs_add (def); - code_globals_add (code_chars_elements); + code_globals_add (code_statements_elements); code_chars_put (name, strlen(name)); code_chars_add ('\0'); + sym.type = TYPE_FUNCTION; + sym.name = util_strdup(name); + sym.offset = function.entry; + asm_symbols_add(sym); /* update assembly state */ + *state = ASM_FUNCTION; util_debug("ASM", "added context function %s to function table\n", name); } - + mem_d(copy); mem_d(name); return true; @@ -371,17 +444,70 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state * CALL* is used (depending on the amount of arguments the function * is expected to take) */ - char *c = NULL; - prog_section_statement s; - size_t i = 0; + enum { + EXPECT_FUNCTION = 1, + EXPECT_VARIABLE = 2, + EXPECT_VALUE = 3 + }; + char *c = (char*)skip; + size_t i = 0; + char expect = 0; + prog_section_statement s; + memset(&s, 0, sizeof(prog_section_statement)); + + /* + * statements are only allowed when inside a function body + * otherwise the assembly is invalid. + */ + if (*state != ASM_FUNCTION) + return false; + + /* + * Skip any possible whitespace, it's not wanted we're searching + * for an instruction. TODO: recrusive decent parser skip on line + * entry instead of pre-op. + */ + while (*skip == ' ' || *skip == '\t') + skip++; + for (; i < sizeof(asm_instr)/sizeof(*asm_instr); i++) { /* * Iterate all possible instructions and check if the selected * instructure in the input stream `skip` is actually a valid * instruction. */ - if (strstr(skip, asm_instr[i].m) == &skip[0]) { + if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) { + + /* + * We hit the end of a function scope, retarget the state + * and add a DONE statement to the statment table. + */ + if (i == AINSTR_END) { + s.opcode = i; + code_statements_add(s); + *state = ASM_NULL; + return true; + } + + /* + * Check the instruction type to see what sort of data + * it's expected to have. + */ + if (i >= INSTR_CALL0 && i <= INSTR_CALL8) + expect = EXPECT_FUNCTION; + else + expect = EXPECT_VARIABLE; + + util_debug( + "ASM", + "found statement %s expecting: `%s` (%ld operand(s))\n", + asm_instr[i].m, + (expect == EXPECT_FUNCTION)?"function name":( + (expect == EXPECT_VARIABLE)?"variable name":( + (expect == EXPECT_VALUE ?"value" : "unknown"))), + asm_instr[i].o + ); /* * Parse the operands for `i` (the instruction). The order * of asm_instr is in the order of the menomic encoding so @@ -393,16 +519,10 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state * Each instruction can have from 0-3 operands; and can * be used with less or more operands depending on it's * selected use. - * + * * DONE for example can use either 0 operands, or 1 (to * emulate the effect of RETURN) - */ - default: - skip += asm_instr[i].l+1; - /* skip whitespace */ - while (*skip == ' ' || *skip == '\t') - skip++; - /* + * * TODO: parse operands correctly figure out what it is * that the assembly is trying to do, i.e string table * lookup, function calls etc. @@ -410,12 +530,57 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state * This needs to have a fall state, we start from the * end of the string and work backwards. */ - case '3': - s.o3.s1 = 0; - case '2': - s.o2.s1 = 0; - case '1': - s.o1.s1 = 0; + #define OPEATS(X,Y) X##Y + #define OPCCAT(X,Y) OPEATS(X,Y) + #define OPLOAD(X,Y) \ + do { \ + util_debug("ASM", "loading operand data ...\n"); \ + if (expect == EXPECT_VARIABLE) { \ + size_t f=0; \ + for (; f