520 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			520 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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.h"
 | |
| #include "ic_lookup.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 */
 | |
| }
 |