/* * Copyright (c) 2001 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU * General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) #ident "$Id: compile.cc,v 1.1 2001/03/11 00:29:38 steve Exp $" #endif # include "compile.h" # include "functor.h" # include "symbols.h" # include "codes.h" # include "schedule.h" # include "vthread.h" # include "parse_misc.h" # include # include # include /* * The opcode table lists all the code mnemonics, along with their * opcode and operand types. The table is written sorted by mnemonic * so that it can be searched by binary search. The opcode_compare * function is a helper function for that lookup. */ enum operand_e { /* Place holder for unused operand */ OA_NONE, /* The operand is a number, an immediate unsigned long integer */ OA_NUMBER, /* The operand is a thread bit index */ OA_BIT, /* The operand is a pointer to code space */ OA_CODE_PTR, /* The operand is a variable or net pointer */ OA_FUNC_PTR }; struct opcode_table_s { const char*mnemonic; vvp_code_fun opcode; unsigned argc; enum operand_e argt[OPERAND_MAX]; }; const static struct opcode_table_s opcode_table[] = { { "%assign", of_ASSIGN, 3, {OA_FUNC_PTR, OA_NUMBER, OA_BIT} }, { "%delay", of_DELAY, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%end", of_END, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%set", of_SET, 2, {OA_FUNC_PTR, OA_BIT, OA_NONE} }, { 0, of_NOOP, 0, {OA_NONE, OA_NONE, OA_NONE} } }; static unsigned opcode_count = 0; static int opcode_compare(const void*k, const void*r) { const char*kp = (const char*)k; const struct opcode_table_s*rp = (const struct opcode_table_s*)r; return strcmp(kp, rp->mnemonic); } /* * Keep a symbol table of addresses within code space. Labels on * executable opcodes are mapped to their address here. */ static symbol_table_t sym_codespace = 0; /* * Keep a symbol table of functors mentioned in the source. This table * is used to resolve references as they come. */ static symbol_table_t sym_functors = 0; /* * If a functor parameter makes a forward reference to a functor, then * I need to save that reference and resolve it after the functors are * created. Use this structure to keep the unresolved references in an * unsorted singly linked list. */ struct resolv_list_s { struct resolv_list_s*next; vvp_ipoint_t port; char*source; }; static struct resolv_list_s*resolv_list = 0; /* * Initialize the compiler by allocation empty symbol tables and * initializing the various address spaces. */ void compile_init(void) { sym_functors = new_symbol_table(); functor_init(); sym_codespace = new_symbol_table(); codespace_init(); opcode_count = 0; while (opcode_table[opcode_count].mnemonic) opcode_count += 1; } /* * The parser calls this function to create a functor. I allocate a * functor, and map the name to the vvp_ipoint_t address for the * functor. Also resolve the inputs to the functor. */ void compile_functor(char*label, char*type, unsigned argc, char**argv) { vvp_ipoint_t fdx = functor_allocate(); functor_t obj = functor_index(fdx); sym_set_value(sym_functors, label, fdx); assert(argc <= 4); /* Run through the arguments looking for the functors that are connected to my input ports. For each source functor that I find, connect the output of that functor to the indexed input by inserting myself (complete with the port number in the vvp_ipoint_t) into the list that the source heads. If the source functor is not declared yet, then don't do the link yet. Save the reference to be resolved later. */ for (unsigned idx = 0 ; idx < argc ; idx += 1) { vvp_ipoint_t tmp = sym_get_value(sym_functors, argv[idx]); if (tmp) { functor_t fport = functor_index(tmp); obj->port[idx] = fport->out; fport->out = ipoint_make(fdx, idx); free(argv[idx]); } else { struct resolv_list_s*res = (struct resolv_list_s*) calloc(1, sizeof(struct resolv_list_s)); res->port = ipoint_make(fdx, idx); res->source = argv[idx]; res->next = resolv_list; resolv_list = res; } } free(argv); free(label); free(type); } /* * The parser uses this function to compile an link an executable * opcode. I do this by looking up the opcode in the opcode_table. The * table gives the operand structure that is acceptible, so I can * process the operands here as well. */ void compile_code(char*label, char*mnem, comp_operands_t opa) { vvp_cpoint_t ptr = codespace_allocate(); /* First, I can give the label a value that is the current codespace pointer. Don't need the text of the label after this is done. */ if (label) { sym_set_value(sym_codespace, label, ptr); free(label); } /* Lookup the opcode in the opcode table. */ struct opcode_table_s*op = (struct opcode_table_s*) bsearch(mnem, opcode_table, opcode_count, sizeof(struct opcode_table_s), &opcode_compare); if (op == 0) { yyerror("Invalid opcode"); return; } assert(op); /* Build up the code from the information about the opcode and the information from the comiler. */ vvp_code_t code = codespace_index(ptr); code->opcode = op->opcode; if (op->argc != (opa? opa->argc : 0)) { yyerror("operand count"); return; } /* Pull the operands that the instruction expects from the list that the parser supplied. */ for (unsigned idx = 0 ; idx < op->argc ; idx += 1) { switch (op->argt[idx]) { case OA_NONE: break; case OA_BIT: if (opa->argv[idx].ltype != L_NUMB) { yyerror("operand format"); break; } code->bit_idx = opa->argv[idx].numb; break; case OA_CODE_PTR: if (opa->argv[idx].ltype != L_TEXT) { yyerror("operand format"); break; } code->cptr = sym_get_value(sym_codespace, opa->argv[idx].text); if (code->cptr == 0) { yyerror("functor undefined"); break; } free(opa->argv[idx].text); break; case OA_FUNC_PTR: if (opa->argv[idx].ltype != L_TEXT) { yyerror("operand format"); break; } code->iptr = sym_get_value(sym_functors, opa->argv[idx].text); if (code->iptr == 0) { yyerror("functor undefined"); break; } free(opa->argv[idx].text); break; case OA_NUMBER: if (opa->argv[idx].ltype != L_NUMB) { yyerror("operand format"); break; } code->number = opa->argv[idx].numb; break; } } if (opa) free(opa); free(mnem); } /* * When the parser finds a thread statement, I create a new thread * with the start address referenced by the program symbol passed to * me. */ void compile_thread(char*start_sym) { vvp_cpoint_t pc = sym_get_value(sym_codespace, start_sym); if (pc == 0) { yyerror("unresolved address"); return; } vthread_t thr = v_newthread(pc); schedule_vthread(thr, 0); free(start_sym); } /* * A variable is a special functor, so we allocate that functor and * write the label into the symbol table. */ void compile_variable(char*label) { vvp_ipoint_t fdx = functor_allocate(); functor_t obj = functor_index(fdx); sym_set_value(sym_functors, label, fdx); free(label); } void compile_cleanup(void) { struct resolv_list_s*tmp_list = resolv_list; resolv_list = 0; while (tmp_list) { struct resolv_list_s*res = tmp_list; tmp_list = res->next; functor_t obj = functor_index(res->port); unsigned idx = ipoint_port(res->port); vvp_ipoint_t tmp = sym_get_value(sym_functors, res->source); if (tmp != 0) { functor_t fport = functor_index(tmp); obj->port[idx] = fport->out; fport->out = res->port; free(res->source); free(res); } else { /* Still not resolved. put back into the list. */ res->next = resolv_list; resolv_list = res; } } } void compile_dump(FILE*fd) { fprintf(fd, "FUNCTOR SYMBOL TABLE:\n"); sym_dump(sym_functors, fd); fprintf(fd, "FUNCTORS:\n"); functor_dump(fd); fprintf(fd, "UNRESOLVED PORT INPUTS:\n"); for (struct resolv_list_s*cur = resolv_list ; cur ; cur = cur->next) fprintf(fd, " %p: %s\n", cur->port, cur->source); fprintf(fd, "CODE SPACE SYMBOL TABLE:\n"); sym_dump(sym_codespace, fd); fprintf(fd, "CODE SPACE DISASSEMBLY:\n"); codespace_dump(fd); } /* * $Log: compile.cc,v $ * Revision 1.1 2001/03/11 00:29:38 steve * Add the vvp engine to cvs. * */