mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2024-11-23 20:33:05 +00:00
More assembler code (less allocations too)
This commit is contained in:
parent
a1a9fc797d
commit
510d795c06
6 changed files with 149 additions and 100 deletions
136
asm.c
136
asm.c
|
@ -102,11 +102,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) {
|
||||
|
@ -194,8 +194,16 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state
|
|||
return false;
|
||||
|
||||
if (strstr(skip, "FUNCTION:") == &skip[0]) {
|
||||
char *copy = util_strsws(skip+10);
|
||||
char *name = util_strchp(copy, strchr(copy, '\0'));
|
||||
char *look = util_strdup(skip+10);
|
||||
char *copy = look;
|
||||
char *name = NULL;
|
||||
while(*copy == ' ' || *copy == '\t') copy++;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
|
@ -380,7 +388,7 @@ static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state
|
|||
* We got valid function structure information now. Lets add
|
||||
* the function to the code writer function table.
|
||||
*/
|
||||
function.entry = code_statements_elements-1;
|
||||
function.entry = code_statements_elements;
|
||||
function.firstlocal = 0;
|
||||
function.locals = 0;
|
||||
function.profile = 0;
|
||||
|
@ -391,7 +399,7 @@ 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_globals_add(code_statements_elements);
|
||||
code_globals_add (code_statements_elements);
|
||||
code_chars_put (name, strlen(name));
|
||||
code_chars_add ('\0');
|
||||
|
||||
|
@ -418,9 +426,18 @@ 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)
|
||||
*/
|
||||
enum {
|
||||
EXPECT_FUNCTION = 1,
|
||||
EXPECT_VARIABLE = 2,
|
||||
EXPECT_VALUE = 3
|
||||
};
|
||||
|
||||
char *c = (char*)skip;
|
||||
prog_section_statement s;
|
||||
size_t i = 0;
|
||||
char expect = 0;
|
||||
prog_section_statement s;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
|
||||
/*
|
||||
* statements are only allowed when inside a function body
|
||||
|
@ -444,7 +461,36 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state
|
|||
* instruction.
|
||||
*/
|
||||
if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) {
|
||||
printf("found statement %s\n", asm_instr[i].m);
|
||||
|
||||
/*
|
||||
* 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 operands)\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
|
||||
|
@ -485,26 +531,44 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state
|
|||
*c = '\0'; \
|
||||
c = (char*)skip; \
|
||||
} while (0)
|
||||
|
||||
case 3: {
|
||||
const char *data; OPFILL(data);
|
||||
printf("OP3: %s\n", data);
|
||||
s.o3.s1 = 0;
|
||||
}
|
||||
case 2: {
|
||||
const char *data; OPFILL(data);
|
||||
printf("OP2: %s\n", data);
|
||||
s.o2.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<assembly_constants_elements; f++) { \
|
||||
if (!strncmp(assembly_constants_data[f].name, (Y), strlen((Y)))) { \
|
||||
(X)=assembly_constants_data[f].offset; \
|
||||
goto OPCCAT(found, __LINE__); \
|
||||
} \
|
||||
} \
|
||||
printf("no variable named %s\n", (Y)); \
|
||||
OPCCAT(found,__LINE__) : \
|
||||
printf("operand loaded for %s\n", (Y)); \
|
||||
break; \
|
||||
} else if (expect == EXPECT_FUNCTION) { \
|
||||
/* \
|
||||
* It's a function call not a variable association with an instruction \
|
||||
* these are harder to handle. \
|
||||
*/ \
|
||||
printf("function calls not implemented, calling `68` for now\n"); \
|
||||
(X)=68; \
|
||||
} \
|
||||
} while (0)
|
||||
case 3: { OPLOAD(s.o3.s1,c); break; }
|
||||
case 2: { OPLOAD(s.o2.s1,c); break; }
|
||||
case 1: {
|
||||
while (*c == ' ' || *c == '\t') c++;
|
||||
c += asm_instr[i].l;
|
||||
while (*c == ' ' || *c == '\t') c++;
|
||||
|
||||
printf("OP1: %s\n", c);
|
||||
s.o1.s1 = 0;
|
||||
OPLOAD(s.o1.s1, c);
|
||||
break;
|
||||
}
|
||||
#undef OPFILL
|
||||
#undef OPLOAD
|
||||
#undef OPCCAT
|
||||
}
|
||||
/* add the statement now */
|
||||
code_statements_add(s);
|
||||
|
@ -515,7 +579,6 @@ static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state
|
|||
|
||||
void asm_parse(FILE *fp) {
|
||||
char *data = NULL;
|
||||
char *skip = NULL;
|
||||
long line = 1; /* current line */
|
||||
size_t size = 0; /* size of line */
|
||||
asm_state state = ASM_NULL;
|
||||
|
@ -523,22 +586,21 @@ void asm_parse(FILE *fp) {
|
|||
#define asm_end(x) \
|
||||
do { \
|
||||
mem_d(data); \
|
||||
mem_d(copy); \
|
||||
line++; \
|
||||
line ++; \
|
||||
util_debug("ASM", x); \
|
||||
} while (0); continue
|
||||
|
||||
while ((data = asm_getline (&size, fp)) != NULL) {
|
||||
char *copy = util_strsws(data); /* skip whitespace */
|
||||
skip = util_strrnl(copy); /* delete newline */
|
||||
char *copy = data;
|
||||
char *skip = copy;
|
||||
while (*copy == ' ' || *copy == '\t') copy++;
|
||||
while (*skip != '\n') skip++;
|
||||
*skip='\0';
|
||||
|
||||
/* TODO: statement END check */
|
||||
if (state == ASM_FUNCTION)
|
||||
state = ASM_NULL;
|
||||
|
||||
if (asm_parse_type(skip, line, &state)){ asm_end("asm_parse_type\n"); }
|
||||
if (asm_parse_func(skip, line, &state)){ asm_end("asm_parse_func\n"); }
|
||||
if (asm_parse_stmt(skip, line, &state)){ asm_end("asm_parse_stmt\n"); }
|
||||
if (asm_parse_type(copy, line, &state)){ asm_end("asm_parse_type\n"); }
|
||||
if (asm_parse_func(copy, line, &state)){ asm_end("asm_parse_func\n"); }
|
||||
if (asm_parse_stmt(copy, line, &state)){ asm_end("asm_parse_stmt\n"); }
|
||||
asm_end("asm_parse_white\n");
|
||||
}
|
||||
#undef asm_end
|
||||
asm_dumps();
|
||||
|
|
12
code.c
12
code.c
|
@ -147,7 +147,7 @@ void code_test() {
|
|||
void code_write() {
|
||||
prog_header code_header;
|
||||
FILE *fp = NULL;
|
||||
size_t it = 1;
|
||||
size_t it = 2;
|
||||
|
||||
/* see proposal.txt */
|
||||
if (opts_omit_nullcode) {}
|
||||
|
@ -231,15 +231,17 @@ void code_write() {
|
|||
if (code_functions_data[it].entry >= 0) {
|
||||
util_debug("GEN", " CODE:\n");
|
||||
for (;;) {
|
||||
if (code_statements_data[j].opcode != INSTR_DONE &&
|
||||
code_statements_data[j].opcode != INSTR_RETURN)
|
||||
util_debug("GEN", " %s {0x%05d,0x%05d,0x%05d}\n",
|
||||
if (code_statements_data[j].opcode != AINSTR_END)
|
||||
util_debug("GEN", " %s {0x%05x,0x%05x,0x%05x}\n",
|
||||
asm_instr[code_statements_data[j].opcode].m,
|
||||
code_statements_data[j].o1.s1,
|
||||
code_statements_data[j].o2.s1,
|
||||
code_statements_data[j].o3.s1
|
||||
);
|
||||
else break;
|
||||
else {
|
||||
util_debug("GEN", " DONE {0x00000,0x00000,0x00000}\n");
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
|
27
data/test.qs
27
data/test.qs
|
@ -89,13 +89,22 @@ FUNCTION: checkextension, $99
|
|||
;code_chars_put("m_toggle", 0x8);
|
||||
;code_chars_put("m_shutdown", 0xA);
|
||||
|
||||
FUNCTION: m_init #3VVV
|
||||
DONE
|
||||
FUNCTION: m_keydown #1S
|
||||
DONE
|
||||
FUNCTION: m_draw #1S
|
||||
DONE
|
||||
FUNCTION: m_toggle #1S
|
||||
DONE
|
||||
FUNCTION: m_shutdown #1S
|
||||
FUNCTION: m_init #0
|
||||
CALL0 dprint
|
||||
END
|
||||
|
||||
FUNCTION: m_keydown #0
|
||||
CALL0 dprint
|
||||
END
|
||||
|
||||
FUNCTION: m_draw #3VVV
|
||||
CALL0 dprint
|
||||
END
|
||||
|
||||
FUNCTION: m_toggle #1S
|
||||
CALL0 dprint
|
||||
END
|
||||
|
||||
FUNCTION: m_shutdown #1S
|
||||
CALL0 dprint
|
||||
END
|
||||
|
|
31
gmqcc.h
31
gmqcc.h
|
@ -492,7 +492,15 @@ enum {
|
|||
INSTR_BITAND,
|
||||
INSTR_BITOR,
|
||||
|
||||
/* Virtual instructions used by the IR
|
||||
/*
|
||||
* Virtual instructions used by the assembler
|
||||
* keep at the end but before virtual instructions
|
||||
* for the IR below.
|
||||
*/
|
||||
AINSTR_END,
|
||||
|
||||
/*
|
||||
* Virtual instructions used by the IR
|
||||
* Keep at the end!
|
||||
*/
|
||||
VINSTR_PHI,
|
||||
|
@ -585,21 +593,22 @@ static const struct {
|
|||
{ "NOT_FNC" , 0, 7 },
|
||||
{ "IF" , 0, 2 },
|
||||
{ "IFNOT" , 0, 5 },
|
||||
{ "CALL0" , 0, 5 },
|
||||
{ "CALL1" , 0, 5 },
|
||||
{ "CALL2" , 0, 5 },
|
||||
{ "CALL3" , 0, 5 },
|
||||
{ "CALL4" , 0, 5 },
|
||||
{ "CALL5" , 0, 5 },
|
||||
{ "CALL6" , 0, 5 },
|
||||
{ "CALL7" , 0, 5 },
|
||||
{ "CALL8" , 0, 5 },
|
||||
{ "CALL0" , 1, 5 },
|
||||
{ "CALL1" , 2, 5 },
|
||||
{ "CALL2" , 3, 5 },
|
||||
{ "CALL3" , 4, 5 },
|
||||
{ "CALL4" , 5, 5 },
|
||||
{ "CALL5" , 6, 5 },
|
||||
{ "CALL6" , 7, 5 },
|
||||
{ "CALL7" , 8, 5 },
|
||||
{ "CALL8" , 9, 5 },
|
||||
{ "STATE" , 0, 5 },
|
||||
{ "GOTO" , 0, 4 },
|
||||
{ "AND" , 0, 3 },
|
||||
{ "OR" , 0, 2 },
|
||||
{ "BITAND" , 0, 6 },
|
||||
{ "BITOR" , 0, 5 }
|
||||
{ "BITOR" , 0, 5 },
|
||||
{ "END" , 0, 3 } /* virtual assembler instruction */
|
||||
};
|
||||
|
||||
void asm_init (const char *, FILE **);
|
||||
|
|
33
util.c
33
util.c
|
@ -137,39 +137,6 @@ char *util_strchp(const char *s, const char *e) {
|
|||
return util_strdup(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove newline from a string (if it exists). This is
|
||||
* done pointer wise instead of strlen(), and an array
|
||||
* access.
|
||||
*/
|
||||
char *util_strrnl(const char *src) {
|
||||
char *cpy = NULL;
|
||||
|
||||
if (src) {
|
||||
cpy = (char*)src;
|
||||
while (*cpy && *cpy != '\n')
|
||||
cpy++;
|
||||
|
||||
*cpy = '\0';
|
||||
}
|
||||
return (char*)src;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes any whitespace prefixed on a string by moving
|
||||
* skipping past it, and stroing the skip distance, so
|
||||
* the string can later be freed (if it was allocated)
|
||||
*/
|
||||
char *util_strsws(const char *skip) {
|
||||
size_t size = 0;
|
||||
if (!skip)
|
||||
return NULL;
|
||||
|
||||
while (*skip == ' ' || *skip == '\t')
|
||||
skip++,size++;
|
||||
return util_strdup(skip-size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if string is all uppercase, otherwise
|
||||
* it returns false.
|
||||
|
|
Loading…
Reference in a new issue