|
@@ -0,0 +1,520 @@
|
|
|
+/* I N T E R M E D I A T E C O D E
|
|
|
+ *
|
|
|
+ * I C . C
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include "../share/types.h"
|
|
|
+#include "../share/debug.h"
|
|
|
+#include "../share/def.h"
|
|
|
+#include "../share/map.h"
|
|
|
+#include "../../../h/em_spec.h"
|
|
|
+#include "../../../h/em_pseu.h"
|
|
|
+#include "../../../h/em_flag.h"
|
|
|
+#include "../../../h/em_mes.h"
|
|
|
+#include "ic_lookup.h"
|
|
|
+#include "ic.h"
|
|
|
+#include "ic_aux.h"
|
|
|
+#include "ic_io.h"
|
|
|
+#include "ic_lib.h"
|
|
|
+#include "../share/alloc.h"
|
|
|
+#include "../share/global.h"
|
|
|
+#include "../share/files.h"
|
|
|
+#include "../share/put.h"
|
|
|
+#include "../share/aux.h"
|
|
|
+
|
|
|
+
|
|
|
+/* Global variables */
|
|
|
+
|
|
|
+
|
|
|
+dblock_p db;
|
|
|
+dblock_p curhol = (dblock_p) 0; /* hol block in current scope */
|
|
|
+dblock_p ldblock; /* last dblock */
|
|
|
+proc_p lproc; /* last proc */
|
|
|
+short tabval; /* used by table1, table2 and table3 */
|
|
|
+offset tabval2;
|
|
|
+char string[IDL+1];
|
|
|
+line_p firstline; /* first line of current procedure */
|
|
|
+line_p lastline; /* last line read */
|
|
|
+int labelcount; /* # labels in current procedure */
|
|
|
+short fragm_type = DUNKNOWN; /* fragm. type: DCON, DROM or DUNKNOWN */
|
|
|
+short fragm_nr = 0; /* fragment number */
|
|
|
+obj_id lastoid = 0;
|
|
|
+proc_id lastpid = 0;
|
|
|
+dblock_id lastdid = 0;
|
|
|
+lab_id lastlid = 0;
|
|
|
+
|
|
|
+offset mespar = UNKNOWN_SIZE;
|
|
|
+ /* argumument of ps_par message of current procedure */
|
|
|
+
|
|
|
+
|
|
|
+extern process_lines();
|
|
|
+extern int readline();
|
|
|
+extern line_p readoperand();
|
|
|
+extern line_p inpseudo();
|
|
|
+
|
|
|
+
|
|
|
+main(argc,argv)
|
|
|
+ int argc;
|
|
|
+ char *argv[];
|
|
|
+{
|
|
|
+ /* The input files must be legal EM Compact
|
|
|
+ * Assembly Language files, as produced by the EM Peephole
|
|
|
+ * Optimizer.
|
|
|
+ * Their file names are passed as arguments.
|
|
|
+ * The output consists of the files:
|
|
|
+ * - lfile: the EM code in Intermediate Code format
|
|
|
+ * - dfile: the data block table file
|
|
|
+ * - pfile: the proc table file
|
|
|
+ * - pdump: the names of all procedures
|
|
|
+ * - ddump: the names of all data blocks
|
|
|
+ */
|
|
|
+
|
|
|
+ FILE *lfile, *dfile, *pfile, *pdump, *ddump;
|
|
|
+
|
|
|
+ lfile = openfile(lname2,"w");
|
|
|
+ pdump = openfile(argv[1],"w");
|
|
|
+ ddump = openfile(argv[2],"w");
|
|
|
+ while (next_file(argc,argv) != NULL) {
|
|
|
+ /* Read all EM input files, process the code
|
|
|
+ * and concatenate all output.
|
|
|
+ */
|
|
|
+ process_lines(lfile);
|
|
|
+ dump_procnames(prochash,NPROCHASH,pdump);
|
|
|
+ dump_dblocknames(symhash,NSYMHASH,ddump);
|
|
|
+ /* Save the names of all procedures that were
|
|
|
+ * first come accross in this file.
|
|
|
+ */
|
|
|
+ cleanprocs(prochash,NPROCHASH,PF_EXTERNAL);
|
|
|
+ cleandblocks(symhash,NSYMHASH,DF_EXTERNAL);
|
|
|
+ /* Make all procedure names that were internal
|
|
|
+ * in this input file invisible.
|
|
|
+ */
|
|
|
+ }
|
|
|
+ fclose(lfile);
|
|
|
+ fclose(pdump);
|
|
|
+ fclose(ddump);
|
|
|
+
|
|
|
+
|
|
|
+ /* remove the remainder of the hashing tables */
|
|
|
+ cleanprocs(prochash,NPROCHASH,0);
|
|
|
+ cleandblocks(symhash,NSYMHASH,0);
|
|
|
+ /* Now write the datablock table and the proctable */
|
|
|
+ dfile = openfile(dname2,"w");
|
|
|
+ putdtable(fdblock, dfile);
|
|
|
+ pfile = openfile(pname2,"w");
|
|
|
+ putptable(fproc, pfile,FALSE);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* Value returned by readline */
|
|
|
+
|
|
|
+#define NORMAL 0
|
|
|
+#define WITH_OPERAND 1
|
|
|
+#define EOFILE 2
|
|
|
+#define PRO_INSTR 3
|
|
|
+#define END_INSTR 4
|
|
|
+#define DELETED_INSTR 5
|
|
|
+
|
|
|
+
|
|
|
+STATIC add_end()
|
|
|
+{
|
|
|
+ /* Add an end-pseudo to the current instruction list */
|
|
|
+
|
|
|
+ lastline->l_next = newline(OPNO);
|
|
|
+ lastline = lastline->l_next;
|
|
|
+ lastline->l_instr = ps_end;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+process_lines(fout)
|
|
|
+ FILE *fout;
|
|
|
+{
|
|
|
+ line_p lnp;
|
|
|
+ short instr;
|
|
|
+ bool eof;
|
|
|
+
|
|
|
+ /* Read and process the code contained in the current file,
|
|
|
+ * on a per procedure basis.
|
|
|
+ * On the fly, fragments are formed. Recall that two
|
|
|
+ * successive CON pseudos are allocated consecutively
|
|
|
+ * in a single fragment, unless these CON pseudos are
|
|
|
+ * separated in the assembly language program by one
|
|
|
+ * of: ROM, BSS, HOL and END (and of course EndOfFile).
|
|
|
+ * The same is true for ROM pseudos.
|
|
|
+ * We keep track of a fragment type (DROM after a ROM
|
|
|
+ * pseudo, DCON after a CON and DUNKNOWN after a HOL,
|
|
|
+ * BSS, END or EndOfFile) and a fragment number (which
|
|
|
+ * is incremented every time we enter a new fragment).
|
|
|
+ * Every data block is assigned such a number
|
|
|
+ * when we come accross its defining occurrence.
|
|
|
+ */
|
|
|
+
|
|
|
+ eof = FALSE;
|
|
|
+ firstline = (line_p) 0;
|
|
|
+ lastline = (line_p) 0;
|
|
|
+ while (!eof) {
|
|
|
+ linecount++; /* for error messages */
|
|
|
+ switch(readline(&instr, &lnp)) {
|
|
|
+ /* read one line, see what kind it is */
|
|
|
+ case WITH_OPERAND:
|
|
|
+ /* instruction with operand, e.g. LOL 10 */
|
|
|
+ lnp = readoperand(instr);
|
|
|
+ lnp->l_instr = instr;
|
|
|
+ /* Fall through! */
|
|
|
+ case NORMAL:
|
|
|
+ VL(lnp);
|
|
|
+ if (lastline != (line_p) 0) {
|
|
|
+ lastline->l_next = lnp;
|
|
|
+ }
|
|
|
+ lastline = lnp;
|
|
|
+ break;
|
|
|
+ case EOFILE:
|
|
|
+ eof = TRUE;
|
|
|
+ fragm_type = DUNKNOWN;
|
|
|
+ if (firstline != (line_p) 0) {
|
|
|
+ add_end();
|
|
|
+ putlines(firstline,fout);
|
|
|
+ firstline = (line_p) 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case PRO_INSTR:
|
|
|
+ VL(lnp);
|
|
|
+ labelcount = 0;
|
|
|
+ if (firstline != lnp) {
|
|
|
+ /* If PRO is not the first
|
|
|
+ * instruction:
|
|
|
+ */
|
|
|
+ add_end();
|
|
|
+ putlines(firstline,fout);
|
|
|
+ firstline = lnp;
|
|
|
+ }
|
|
|
+ lastline = lnp;
|
|
|
+ break;
|
|
|
+ case END_INSTR:
|
|
|
+ curproc->p_nrformals = mespar;
|
|
|
+ mespar = UNKNOWN_SIZE;
|
|
|
+ assert(lastline != (line_p) 0);
|
|
|
+ lastline->l_next = lnp;
|
|
|
+ putlines(firstline,fout);
|
|
|
+ /* write and delete code */
|
|
|
+ firstline = (line_p) 0;
|
|
|
+ lastline = (line_p) 0;
|
|
|
+ cleaninstrlabs();
|
|
|
+ /* scope of instruction labels ends here,
|
|
|
+ * so forget about them.
|
|
|
+ */
|
|
|
+ fragm_type = DUNKNOWN;
|
|
|
+ break;
|
|
|
+ case DELETED_INSTR:
|
|
|
+ /* EXP, INA etc. are deleted */
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ error("illegal readline");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int readline(instr_out, lnp_out)
|
|
|
+ short *instr_out;
|
|
|
+ line_p *lnp_out;
|
|
|
+{
|
|
|
+ register line_p lnp;
|
|
|
+ short n;
|
|
|
+
|
|
|
+ /* Read one line. If it is a normal EM instruction without
|
|
|
+ * operand, we can allocate a line struct for it here.
|
|
|
+ * If so, return a pointer to it via lnp_out, else just
|
|
|
+ * return the instruction code via instr_out.
|
|
|
+ */
|
|
|
+
|
|
|
+ VA((short *) instr_out);
|
|
|
+ VA((short *) lnp_out);
|
|
|
+ switch(table1()) {
|
|
|
+ /* table1 sets string, tabval or tabval2 and
|
|
|
+ * returns an indication of what was read.
|
|
|
+ */
|
|
|
+ case ATEOF:
|
|
|
+ return EOFILE;
|
|
|
+ case INST:
|
|
|
+ *instr_out = tabval; /* instruction code */
|
|
|
+ return WITH_OPERAND;
|
|
|
+ case DLBX:
|
|
|
+ /* data label defining occurrence, precedes
|
|
|
+ * a data block.
|
|
|
+ */
|
|
|
+ db = block_of_lab(string);
|
|
|
+ /* global variable, used by inpseudo */
|
|
|
+ lnp = newline(OPSHORT);
|
|
|
+ SHORT(lnp) = (short) db->d_id;
|
|
|
+ lnp->l_instr = ps_sym;
|
|
|
+ *lnp_out = lnp;
|
|
|
+ if (firstline == (line_p) 0) {
|
|
|
+ firstline = lnp;
|
|
|
+ /* only a pseudo (e.g. PRO) or data label
|
|
|
+ * can be the first instruction.
|
|
|
+ */
|
|
|
+ }
|
|
|
+ return NORMAL;
|
|
|
+ case ILBX:
|
|
|
+ /* instruction label defining occurrence */
|
|
|
+ labelcount++;
|
|
|
+ lnp = newline(OPINSTRLAB);
|
|
|
+ lnp->l_instr = op_lab;
|
|
|
+ INSTRLAB(lnp) = instr_lab(tabval);
|
|
|
+ *lnp_out = lnp;
|
|
|
+ return NORMAL;
|
|
|
+ case PSEU:
|
|
|
+ n = tabval;
|
|
|
+ lnp = inpseudo(n); /* read a pseudo */
|
|
|
+ if (lnp == (line_p) 0) return DELETED_INSTR;
|
|
|
+ *lnp_out = lnp;
|
|
|
+ lnp->l_instr = n;
|
|
|
+ if (firstline == (line_p) 0) {
|
|
|
+ firstline = lnp;
|
|
|
+ /* only a pseudo (e.g. PRO) or data label
|
|
|
+ * can be the first instruction.
|
|
|
+ */
|
|
|
+ }
|
|
|
+ if (n == ps_end) return END_INSTR;
|
|
|
+ if (n == ps_pro) return PRO_INSTR;
|
|
|
+ return NORMAL;
|
|
|
+ }
|
|
|
+ /* NOTREACHED */
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+line_p readoperand(instr)
|
|
|
+ short instr;
|
|
|
+{
|
|
|
+ /* Read the operand of the given instruction.
|
|
|
+ * Create a line struct and return a pointer to it.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+ register line_p lnp;
|
|
|
+ short flag;
|
|
|
+
|
|
|
+ VI(instr);
|
|
|
+ flag = em_flag[ instr - sp_fmnem] & EM_PAR;
|
|
|
+ if (flag == PAR_NO) {
|
|
|
+ return (newline(OPNO));
|
|
|
+ }
|
|
|
+ switch(table2()) {
|
|
|
+ case sp_cend:
|
|
|
+ return(newline(OPNO));
|
|
|
+ case CSTX1:
|
|
|
+ /* constant */
|
|
|
+ /* If the instruction has the address
|
|
|
+ * of an external variable as argument,
|
|
|
+ * the constant must be regarded as an
|
|
|
+ * offset in the current hol block,
|
|
|
+ * so an object must be created.
|
|
|
+ * Similarly, the instruction may have
|
|
|
+ * an instruction label as argument.
|
|
|
+ */
|
|
|
+ switch(flag) {
|
|
|
+ case PAR_G:
|
|
|
+ lnp = newline(OPOBJECT);
|
|
|
+ OBJ(lnp) =
|
|
|
+ object((char *) 0,(offset) tabval,
|
|
|
+ opr_size(instr));
|
|
|
+ break;
|
|
|
+ case PAR_B:
|
|
|
+ lnp = newline(OPINSTRLAB);
|
|
|
+ INSTRLAB(lnp) = instr_lab(tabval);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ lnp = newline(OPSHORT);
|
|
|
+ SHORT(lnp) = tabval;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+#ifdef LONGOFF
|
|
|
+ case CSTX2:
|
|
|
+ /* double constant */
|
|
|
+ lnp = newline(OPOFFSET);
|
|
|
+ OFFSET(lnp) = tabval2;
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ case ILBX:
|
|
|
+ /* applied occurrence instruction label */
|
|
|
+ lnp = newline(OPINSTRLAB);
|
|
|
+ INSTRLAB(lnp) = instr_lab(tabval);
|
|
|
+ break;
|
|
|
+ case DLBX:
|
|
|
+ /* applied occurrence data label */
|
|
|
+ lnp = newline(OPOBJECT);
|
|
|
+ OBJ(lnp) = object(string, (offset) 0,
|
|
|
+ opr_size(instr) );
|
|
|
+ break;
|
|
|
+ case VALX1:
|
|
|
+ lnp = newline(OPOBJECT);
|
|
|
+ OBJ(lnp) = object(string, (offset) tabval,
|
|
|
+ opr_size(instr) );
|
|
|
+ break;
|
|
|
+#ifdef LONGOFF
|
|
|
+ case VALX2:
|
|
|
+ lnp = newline(OPOBJECT);
|
|
|
+ OBJ(lnp) = object(string,tabval2,
|
|
|
+ opr_size(instr) );
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ case sp_pnam:
|
|
|
+ lnp = newline(OPPROC);
|
|
|
+ PROC(lnp) = proclookup(string,OCCURRING);
|
|
|
+ VP(PROC(lnp));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ assert(FALSE);
|
|
|
+ }
|
|
|
+ return lnp;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+line_p inpseudo(n)
|
|
|
+ short n;
|
|
|
+{
|
|
|
+ int m;
|
|
|
+ line_p lnp;
|
|
|
+ byte pseu;
|
|
|
+ short nlast;
|
|
|
+
|
|
|
+ /* Read the (remainder of) a pseudo instruction, the instruction
|
|
|
+ * code of which is n. The END pseudo may be deleted (return 0).
|
|
|
+ * The pseudos INA, EXA, INP and EXP (visibility pseudos) must
|
|
|
+ * also be deleted, although the effects they have on the
|
|
|
+ * visibility of global names and procedure names must first
|
|
|
+ * be recorded in the datablock or procedure table.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+ switch(n) {
|
|
|
+ case ps_hol:
|
|
|
+ case ps_bss:
|
|
|
+ case ps_rom:
|
|
|
+ case ps_con:
|
|
|
+ if (lastline == (line_p) 0 || !is_datalabel(lastline)) {
|
|
|
+ if (n == ps_hol) {
|
|
|
+ /* A HOL need not be preceded
|
|
|
+ * by a label.
|
|
|
+ */
|
|
|
+ curhol = db = block_of_lab((char *) 0);
|
|
|
+ } else {
|
|
|
+ assert(lastline != (line_p) 0);
|
|
|
+ nlast = INSTR(lastline);
|
|
|
+ if (n == nlast &&
|
|
|
+ (n == ps_rom || n == ps_con)) {
|
|
|
+ /* Two successive roms/cons are
|
|
|
+ * combined into one data block
|
|
|
+ * if the second is not preceded by
|
|
|
+ * a data label.
|
|
|
+ */
|
|
|
+ lnp = arglist(0);
|
|
|
+ pseu = (byte) (n == ps_rom?DROM:DCON);
|
|
|
+ combine(db,lastline,lnp,pseu);
|
|
|
+ oldline(lnp);
|
|
|
+ return (line_p) 0;
|
|
|
+ } else {
|
|
|
+ error("datablock without label");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ VD(db);
|
|
|
+ m = (n == ps_hol || n == ps_bss ? 3 : 0);
|
|
|
+ lnp = arglist(m);
|
|
|
+ /* Read the arguments, 3 for hol or bss and a list
|
|
|
+ * of undetermined length for rom and con.
|
|
|
+ */
|
|
|
+ dblockdef(db,n,lnp);
|
|
|
+ /* Fill in d_pseudo, d_size and d_values fields of db */
|
|
|
+ if (fragm_type != db->d_pseudo & BMASK) {
|
|
|
+ /* Keep track of fragment numbers,
|
|
|
+ * enter a new fragment.
|
|
|
+ */
|
|
|
+ fragm_nr++;
|
|
|
+ switch(db->d_pseudo) {
|
|
|
+ case DCON:
|
|
|
+ case DROM:
|
|
|
+ fragm_type = db->d_pseudo;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ fragm_type = DUNKNOWN;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ db->d_fragmnr = fragm_nr;
|
|
|
+ return lnp;
|
|
|
+ case ps_ina:
|
|
|
+ getsym(DEFINING);
|
|
|
+ /* Read and lookup a symbol. As this must be
|
|
|
+ * the first occurrence of the symbol and we
|
|
|
+ * say it's a defining occurrence, getsym will
|
|
|
+ * automatically make it internal (according to
|
|
|
+ * the EM visibility rules).
|
|
|
+ * The result (a dblock pointer) is voided.
|
|
|
+ */
|
|
|
+ return (line_p) 0;
|
|
|
+ case ps_inp:
|
|
|
+ getproc(DEFINING); /* same idea */
|
|
|
+ return (line_p) 0;
|
|
|
+ case ps_exa:
|
|
|
+ getsym(OCCURRING);
|
|
|
+ return (line_p) 0;
|
|
|
+ case ps_exp:
|
|
|
+ getproc(OCCURRING);
|
|
|
+ return (line_p) 0;
|
|
|
+ case ps_pro:
|
|
|
+ curproc = getproc(DEFINING);
|
|
|
+ /* This is a real defining occurrence of a proc */
|
|
|
+ curproc->p_localbytes = get_off();
|
|
|
+ curproc->p_flags1 |= PF_BODYSEEN;
|
|
|
+ /* Record the fact that we came accross
|
|
|
+ * the body of this procedure.
|
|
|
+ */
|
|
|
+ lnp = newline(OPPROC);
|
|
|
+ PROC(lnp) = curproc;
|
|
|
+ lnp->l_instr = (byte) ps_pro;
|
|
|
+ return lnp;
|
|
|
+ case ps_end:
|
|
|
+ curproc->p_nrlabels = labelcount;
|
|
|
+ lnp = newline(OPNO);
|
|
|
+ get_off();
|
|
|
+ /* Void # localbytes, which we already know
|
|
|
+ * from the PRO instruction.
|
|
|
+ */
|
|
|
+ return lnp;
|
|
|
+ case ps_mes:
|
|
|
+ lnp = arglist(0);
|
|
|
+ switch((int) aoff(ARG(lnp),0)) {
|
|
|
+ case ms_err:
|
|
|
+ error("ms_err encountered");
|
|
|
+ case ms_opt:
|
|
|
+ error("ms_opt encountered");
|
|
|
+ case ms_emx:
|
|
|
+ ws = aoff(ARG(lnp),1);
|
|
|
+ ps = aoff(ARG(lnp),2);
|
|
|
+ break;
|
|
|
+ case ms_ext:
|
|
|
+ /* this message was already processed
|
|
|
+ * by the lib package
|
|
|
+ */
|
|
|
+ case ms_src:
|
|
|
+ /* Don't bother about linecounts */
|
|
|
+ oldline(lnp);
|
|
|
+ return (line_p) 0;
|
|
|
+ case ms_par:
|
|
|
+ mespar = aoff(ARG(lnp),1);
|
|
|
+ /* #bytes of parameters of current proc */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return lnp;
|
|
|
+ default:
|
|
|
+ assert(FALSE);
|
|
|
+ }
|
|
|
+ /* NOTREACHED */
|
|
|
+}
|