* itbl-parse.y: Fix indentation mistakes from indent program.

* itbl-lex.l: Fix indentation mistakes from indent program.
	* itbl-ops.h: Add include for ansidecl.h.
	Add PARAMS around function arguments.
	Add declaration for itbl_have_entries.
	* itbl-ops.c: Add PARAMS around function arguments.
	* Makefile.in: Add itbl build rules.
	Add dependancies for itbl files to mips target.
	* as.c: Add itbl support.
	Add new option "--insttbl" for dynamically extending instruction set.
	* as.h: Declare insttbl_file_name;
	the name of file defining extensions to the basic instruction set
	* configure.in, configure: Add itbl-parse.o, itbl-lex.o, and
	itbl-ops.o to extra_objects for mips configuration.
	Add include file link from itbl-cpu.h to
	config/itbl-${target_cpu_type}.h.
	* config/tc-mips.c: Allow copz instructions.
	Add notes for future additions to the itbl support.
	Add debug macros.
	(macro): Call itbl_assemble to assemble itbl instructions.
	See if an unknown register is specified in an itbl entry.
This commit is contained in:
Dawn Perchik 1997-02-23 22:23:12 +00:00
parent c7583da0b6
commit efec4a282c
10 changed files with 1322 additions and 806 deletions

View file

@ -1,3 +1,27 @@
Sat Feb 22 21:25:00 1997 Dawn Perchik <dawn@cygnus.com>
* itbl-parse.y: Fix indentation mistakes from indent program.
* itbl-lex.l: Fix indentation mistakes from indent program.
* itbl-ops.h: Add include for ansidecl.h.
Add PARAMS around function arguments.
Add declaration for itbl_have_entries.
* itbl-ops.c: Add PARAMS around function arguments.
* Makefile.in: Add itbl build rules.
Add dependancies for itbl files to mips target.
* as.c: Add itbl support.
Add new option "--insttbl" for dynamically extending instruction set.
* as.h: Declare insttbl_file_name;
the name of file defining extensions to the basic instruction set
* configure.in, configure: Add itbl-parse.o, itbl-lex.o, and
itbl-ops.o to extra_objects for mips configuration.
Add include file link from itbl-cpu.h to
config/itbl-${target_cpu_type}.h.
* config/tc-mips.c: Allow copz instructions.
Add notes for future additions to the itbl support.
Add debug macros.
(macro): Call itbl_assemble to assemble itbl instructions.
See if an unknown register is specified in an itbl entry.
Sat Feb 22 20:53:01 1997 Fred Fish <fnf@cygnus.com> Sat Feb 22 20:53:01 1997 Fred Fish <fnf@cygnus.com>
* doc/internals.texi (CPU backend): Fix typo in md_section_align * doc/internals.texi (CPU backend): Fix typo in md_section_align
description. description.

View file

@ -62,7 +62,7 @@ INSTALL_DATA = @INSTALL_DATA@
INSTALL_XFORM = $(INSTALL) -t='$(program_transform_name)' INSTALL_XFORM = $(INSTALL) -t='$(program_transform_name)'
INSTALL_XFORM1= $(INSTALL_XFORM) -b=.1 INSTALL_XFORM1= $(INSTALL_XFORM) -b=.1
DISTSTUFF= make-gas.com m68k-parse.c DISTSTUFF= make-gas.com m68k-parse.c itbl-parse.y itbl-lex.l itbl-ops.c
AR = ar AR = ar
AR_FLAGS = qv AR_FLAGS = qv
@ -72,11 +72,11 @@ MAKEINFO = makeinfo
TEXI2DVI = texi2dvi TEXI2DVI = texi2dvi
RANLIB = ranlib RANLIB = ranlib
CC = @CC@ CC = @CC@
CFLAGS = -g
LDFLAGS =
HLDFLAGS = @HLDFLAGS@ HLDFLAGS = @HLDFLAGS@
HLDENV = @HLDENV@ HLDENV = @HLDENV@
RPATH_ENVVAR = @RPATH_ENVVAR@ RPATH_ENVVAR = @RPATH_ENVVAR@
CFLAGS = -g
LDFLAGS =
MAKEOVERRIDES= MAKEOVERRIDES=
@ -107,6 +107,12 @@ RUNTEST = `if [ -f $${srcdir}/../dejagnu/runtest ] ; then \
fi` fi`
RUNTESTFLAGS= RUNTESTFLAGS=
# use @target_cpu_type@ for refering to configured target name
IT_HDRS=itbl-parse.h $(srcdir)/itbl-ops.h itbl-cpu.h
IT_SRCS=itbl-parse.c itbl-lex.c $(srcdir)/itbl-ops.c
IT_DEPS=$(srcdir)/itbl-parse.y $(srcdir)/itbl-lex.l $(srcdir)/config/itbl-@target_cpu_type@.h
IT_OBJS=itbl-parse.o itbl-lex.o itbl-ops.o
# Lists of files for various purposes. # Lists of files for various purposes.
REAL_SOURCES = \ REAL_SOURCES = \
@ -123,6 +129,7 @@ REAL_SOURCES = \
$(srcdir)/hash.c \ $(srcdir)/hash.c \
$(srcdir)/input-file.c \ $(srcdir)/input-file.c \
$(srcdir)/input-scrub.c \ $(srcdir)/input-scrub.c \
$(srcdir)/itbl-ops.c \
$(srcdir)/literal.c \ $(srcdir)/literal.c \
$(srcdir)/messages.c \ $(srcdir)/messages.c \
$(srcdir)/output-file.c \ $(srcdir)/output-file.c \
@ -150,6 +157,7 @@ REAL_HEADERS = \
$(srcdir)/frags.h \ $(srcdir)/frags.h \
$(srcdir)/hash.h \ $(srcdir)/hash.h \
$(srcdir)/input-file.h \ $(srcdir)/input-file.h \
$(srcdir)/itbl-ops.h \
$(srcdir)/listing.h \ $(srcdir)/listing.h \
$(srcdir)/tc.h \ $(srcdir)/tc.h \
$(srcdir)/obj.h \ $(srcdir)/obj.h \
@ -164,7 +172,8 @@ LINKED_HEADERS = \
targ-env.h \ targ-env.h \
targ-cpu.h \ targ-cpu.h \
obj-format.h \ obj-format.h \
atof-targ.h atof-targ.h \
itbl-cpu.h
HEADERS = $(LINKED_HEADERS) $(REAL_HEADERS) HEADERS = $(LINKED_HEADERS) $(REAL_HEADERS)
@ -259,7 +268,7 @@ as.new: $(OBJS) $(LIBDEPS)
$(OBJS): config.h as.h targ-env.h obj-format.h targ-cpu.h flonum.h expr.h \ $(OBJS): config.h as.h targ-env.h obj-format.h targ-cpu.h flonum.h expr.h \
struc-symbol.h write.h frags.h hash.h read.h symbols.h tc.h obj.h \ struc-symbol.h write.h frags.h hash.h read.h symbols.h tc.h obj.h \
listing.h bignum.h $(srcdir)/../include/libiberty.h listing.h bignum.h $(IT_HDRS) $(srcdir)/../include/libiberty.h
gasp.new: $(GASPOBJS) ../libiberty/libiberty.a gasp.new: $(GASPOBJS) ../libiberty/libiberty.a
$(CC) $(ALL_CFLAGS) $(LDFLAGS) -o gasp.new $(GASPOBJS) ../libiberty/libiberty.a $(LOADLIBES) $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o gasp.new $(GASPOBJS) ../libiberty/libiberty.a $(LOADLIBES)
@ -345,7 +354,8 @@ TARG_CPU_DEP_i960 =
TARG_CPU_DEP_m68k = $(srcdir)/../include/opcode/m68k.h \ TARG_CPU_DEP_m68k = $(srcdir)/../include/opcode/m68k.h \
$(srcdir)/config/m68k-parse.h subsegs.h $(srcdir)/config/m68k-parse.h subsegs.h
TARG_CPU_DEP_m88k = $(srcdir)/config/m88k-opcode.h subsegs.h TARG_CPU_DEP_m88k = $(srcdir)/config/m88k-opcode.h subsegs.h
TARG_CPU_DEP_mips = $(srcdir)/../include/opcode/mips.h subsegs.h TARG_CPU_DEP_mips = $(srcdir)/../include/opcode/mips.h subsegs.h \
$(srcdir)/config/itbl-mips.h
TARG_CPU_DEP_ns32k = TARG_CPU_DEP_ns32k =
TARG_CPU_DEP_ppc = subsegs.h TARG_CPU_DEP_ppc = subsegs.h
TARG_CPU_DEP_sh = $(srcdir)/../opcodes/sh-opc.h subsegs.h TARG_CPU_DEP_sh = $(srcdir)/../opcodes/sh-opc.h subsegs.h
@ -388,7 +398,7 @@ ecoff.o : ecoff.c ecoff.h \
stabs.o : stabs.c subsegs.h $(srcdir)/../include/aout/stab_gnu.h stabs.o : stabs.c subsegs.h $(srcdir)/../include/aout/stab_gnu.h
atof-targ.o : atof-targ.c atof-targ.o : atof-targ.c
obj-format.o : obj-format.c obj-format.o : obj-format.c
targ-cpu.o : targ-cpu.c $(TARG_CPU_DEP_@target_cpu_type@) targ-cpu.o : targ-cpu.c $(TARG_CPU_DEP_@target_cpu_type@) $(IT_HDRS)
obj-elf.o : $(srcdir)/config/obj-elf.c obj-elf.o : $(srcdir)/config/obj-elf.c
$(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) $(srcdir)/config/obj-elf.c $(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) $(srcdir)/config/obj-elf.c
@ -407,6 +417,51 @@ m68k-parse.c: $(srcdir)/config/m68k-parse.y
mv -f y.tab.c m68k-parse.c mv -f y.tab.c m68k-parse.c
m68k-parse.o: m68k-parse.c $(srcdir)/config/m68k-parse.h m68k-parse.o: m68k-parse.c $(srcdir)/config/m68k-parse.h
# The instruction table specification lexical analyzer and parser.
itbl-cpu.h : $(srcdir)/config/itbl-@target_cpu_type@.h
itbl-parse.h : $(srcdir)/itbl-parse.y
itbl-parse.c : $(srcdir)/itbl-parse.y
itbl-lex.c : $(srcdir)/itbl-lex.l
itbl-lex.c: $(srcdir)/itbl-lex.l
$(LEX) $(LEXFLAGS) $(srcdir)/itbl-lex.l
mv -f lex.yy.c itbl-lex.c
itbl-lex.o: itbl-lex.c
$(CC) -Wall -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) itbl-lex.c
itbl-parse.c itbl-parse.h: $(srcdir)/itbl-parse.y
$(YACC) -d $(YACCFLAGS) $(srcdir)/itbl-parse.y
mv -f y.tab.c itbl-parse.c
mv -f y.tab.h itbl-parse.h
itbl-parse.o: itbl-parse.c itbl-parse.h $(srcdir)/itbl-ops.h itbl-cpu.h
$(CC) -Wall -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) itbl-parse.c
itbl-ops.o: $(srcdir)/itbl-ops.c \
$(srcdir)/itbl-ops.h itbl-cpu.h itbl-parse.h
$(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) $(srcdir)/itbl-ops.c
# stand-alone assembler & disassembler
itbl-test-ops.o: $(srcdir)/itbl-ops.c \
$(srcdir)/itbl-ops.h itbl-cpu.h itbl-parse.h
$(CC) -o itbl-test-ops.o -DSTAND_ALONE -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) $(srcdir)/itbl-ops.c
itbl-test.o: $(srcdir)/itbl-test.c $(srcdir)/itbl-ops.h itbl-cpu.h
$(CC) -c -DSTAND_ALONE $(ALL_CFLAGS) $(INCLUDES) $(srcdir)/itbl-test.c
IT_TEST_OBJS= itbl-parse.o itbl-lex.o itbl-test-ops.o
itbl-test: $(IT_TEST_OBJS) itbl-test.o $(LIBDEPS)
$(CC) $(ALL_CFLAGS) $(LDFLAGS) -o itbl-test itbl-test.o $(IT_TEST_OBJS) $(LIBS)
# target itbl definitions for configuring coprocessor itbl support.
# configure should have taken care of this for us...
itbl-cpu.h: $(srcdir)/config/itbl-@target_cpu_type@.h
ln -s $(srcdir)/config/itbl-@target_cpu_type@.h itbl-cpu.h
# Remake the info files. # Remake the info files.
doc: $(srcdir)/as.info doc: $(srcdir)/as.info
@ -428,6 +483,7 @@ clean mostlyclean: clean-here
DISTCLEAN_HERE = config.status Makefile targ-env.h targ-cpu.h \ DISTCLEAN_HERE = config.status Makefile targ-env.h targ-cpu.h \
targ-cpu.c obj-format.h obj-format.c atof-targ.c TAGS \ targ-cpu.c obj-format.h obj-format.c atof-targ.c TAGS \
atof-targ.h itbl-cpu.h \
config-stamp config.h conf config.log config.cache .gdbinit \ config-stamp config.h conf config.log config.cache .gdbinit \
testsuite/Makefile testsuite/config.status testsuite/Makefile testsuite/config.status

View file

@ -1,5 +1,5 @@
/* as.c - GAS main program. /* as.c - GAS main program.
Copyright (C) 1987, 90, 91, 92, 93, 94, 95, 1996 Copyright (C) 1987, 90, 91, 92, 93, 94, 95, 96, 1997
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler. This file is part of GAS, the GNU Assembler.
@ -35,7 +35,6 @@
*/ */
#include "ansidecl.h" #include "ansidecl.h"
#include "libiberty.h"
#define COMMON #define COMMON
@ -85,6 +84,16 @@ struct defsym_list
}; };
static struct defsym_list *defsyms; static struct defsym_list *defsyms;
/* Keep a record of the itbl files we read in. */
struct itbl_file_list
{
struct itbl_file_list *next;
char *name;
};
static struct itbl_file_list *itbl_files;
void void
print_version_id () print_version_id ()
@ -134,6 +143,8 @@ Options:\n\
--statistics print various measured statistics from execution\n\ --statistics print various measured statistics from execution\n\
--version print assembler version number and exit\n\ --version print assembler version number and exit\n\
-W suppress warnings\n\ -W suppress warnings\n\
-t,--itbl INSTTBL extend instruction set to include instrictions\n\
matching the specifications defined in file INSTTBL\n\
-w ignored\n\ -w ignored\n\
-X ignored\n\ -X ignored\n\
-Z generate object file even after errors\n"); -Z generate object file even after errors\n");
@ -267,6 +278,8 @@ parse_args (pargc, pargv)
'v', 'v',
#endif #endif
'w', 'X', 'w', 'X',
/* New option for extending instruction set (see also --itbl below) */
't',
'\0' '\0'
}; };
struct option *longopts; struct option *longopts;
@ -289,7 +302,15 @@ parse_args (pargc, pargv)
#define OPTION_EMULATION (OPTION_STD_BASE + 6) #define OPTION_EMULATION (OPTION_STD_BASE + 6)
{"emulation", required_argument, NULL, OPTION_EMULATION}, {"emulation", required_argument, NULL, OPTION_EMULATION},
#define OPTION_DEFSYM (OPTION_STD_BASE + 7) #define OPTION_DEFSYM (OPTION_STD_BASE + 7)
{"defsym", required_argument, NULL, OPTION_DEFSYM} {"defsym", required_argument, NULL, OPTION_DEFSYM},
#define OPTION_INSTTBL (OPTION_STD_BASE + 8)
/* New option for extending instruction set (see also -t above).
* The "-t file" or "--itbl file" option extends the basic set
* of valid instructions by reading "file", a text file containing
* a list of instruction formats. The additional opcodes and their
* formats are added to the built-in set of instructions, and
* mnemonics for new registers may also be defined. */
{"itbl", required_argument, NULL, OPTION_INSTTBL}
}; };
/* Construct the option lists from the standard list and the /* Construct the option lists from the standard list and the
@ -422,6 +443,32 @@ the GNU General Public License. This program has absolutely no warranty.\n");
} }
break; break;
case OPTION_INSTTBL:
case 't':
{
/* optarg is the name of the file containing the instruction
formats, opcodes, register names, etc. */
struct itbl_file_list *n;
n = (struct itbl_file_list *) xmalloc (sizeof *n);
n->next = itbl_files;
n->name = optarg;
itbl_files = n;
/* Parse the file and add the new instructions to our internal
table. If multiple instruction tables are specified, the
information from this table gets appended onto the existing
internal table. */
itbl_files->name = xstrdup (optarg);
if (itbl_parse(itbl_files->name) != 0)
{
fprintf (stderr, "Failed to read instruction table %s\n",
itbl_files->name);
exit (EXIT_SUCCESS);
}
}
break;
case 'J': case 'J':
flag_signed_overflow_ok = 1; flag_signed_overflow_ok = 1;
break; break;
@ -543,6 +590,7 @@ main (argc, argv)
start_time = get_run_time (); start_time = get_run_time ();
if (debug_memory) if (debug_memory)
{ {
#ifdef BFD_ASSEMBLER #ifdef BFD_ASSEMBLER
@ -614,6 +662,8 @@ main (argc, argv)
tc_init_after_args (); tc_init_after_args ();
#endif #endif
itbl_init ();
/* Now that we have fully initialized, and have created the output /* Now that we have fully initialized, and have created the output
file, define any symbols requested by --defsym command line file, define any symbols requested by --defsym command line
arguments. */ arguments. */
@ -639,8 +689,7 @@ main (argc, argv)
#endif #endif
if (seen_at_least_1_file () if (seen_at_least_1_file ()
&& !((had_warnings () && flag_always_generate_output) && (flag_always_generate_output || had_errors () == 0))
|| had_errors () > 0))
keep_it = 1; keep_it = 1;
else else
keep_it = 0; keep_it = 0;
@ -659,6 +708,9 @@ main (argc, argv)
output_file_close (out_file_name); output_file_close (out_file_name);
#endif #endif
if (had_errors () > 0 && ! flag_always_generate_output)
keep_it = 0;
if (!keep_it) if (!keep_it)
unlink (out_file_name); unlink (out_file_name);
@ -668,8 +720,7 @@ main (argc, argv)
/* Use xexit instead of return, because under VMS environments they /* Use xexit instead of return, because under VMS environments they
may not place the same interpretation on the value given. */ may not place the same interpretation on the value given. */
if ((had_warnings () && flag_always_generate_output) if (had_errors () > 0)
|| had_errors () > 0)
xexit (EXIT_FAILURE); xexit (EXIT_FAILURE);
xexit (EXIT_SUCCESS); xexit (EXIT_SUCCESS);
} }

View file

@ -36,6 +36,13 @@
#endif #endif
#include "opcode/mips.h" #include "opcode/mips.h"
#include "itbl-ops.h"
#ifdef DEBUG
#define DBG(x) printf x
#else
#define DBG(x)
#endif
#ifdef OBJ_MAYBE_ELF #ifdef OBJ_MAYBE_ELF
/* Clean up namespace so we can include obj-elf.h too. */ /* Clean up namespace so we can include obj-elf.h too. */
@ -159,6 +166,7 @@ static int interlocks = -1;
/* As with "interlocks" this is used by hardware that has FP /* As with "interlocks" this is used by hardware that has FP
(co-processor) interlocks. */ (co-processor) interlocks. */
/* Itbl support may require additional care here. */
static int cop_interlocks = -1; static int cop_interlocks = -1;
/* MIPS PIC level. */ /* MIPS PIC level. */
@ -872,6 +880,7 @@ md_begin ()
else else
interlocks = 0; interlocks = 0;
/* Itbl support may require additional care here. */
if (mips_cpu == 4300) if (mips_cpu == 4300)
cop_interlocks = 1; cop_interlocks = 1;
else else
@ -1099,7 +1108,11 @@ md_assemble (str)
if (mips16) if (mips16)
mips16_ip (str, &insn); mips16_ip (str, &insn);
else else
{
mips_ip (str, &insn); mips_ip (str, &insn);
DBG(("returned from mips_ip(%s) insn_opcode = 0x%x\n",
str, insn.insn_opcode));
}
if (insn_error) if (insn_error)
{ {
@ -1221,6 +1234,7 @@ reg_needs_delay (reg)
delays delay the use of general register rt for one delays delay the use of general register rt for one
instruction on the r3000. The r6000 and r4000 use instruction on the r3000. The r6000 and r4000 use
interlocks. */ interlocks. */
/* Itbl support may require additional care here. */
know (prev_pinfo & INSN_WRITE_GPR_T); know (prev_pinfo & INSN_WRITE_GPR_T);
if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)) if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
return 1; return 1;
@ -1314,6 +1328,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
delays delay the use of general register rt for one delays delay the use of general register rt for one
instruction on the r3000. The r6000 and r4000 use instruction on the r3000. The r6000 and r4000 use
interlocks. */ interlocks. */
/* Itbl support may require additional care here. */
know (prev_pinfo & INSN_WRITE_GPR_T); know (prev_pinfo & INSN_WRITE_GPR_T);
if (mips_optimize == 0 if (mips_optimize == 0
|| insn_uses_reg (ip, || insn_uses_reg (ip,
@ -1343,6 +1358,9 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
knowledge of CP0 handling, and the coprocessors other knowledge of CP0 handling, and the coprocessors other
than the floating point unit are not distinguished at than the floating point unit are not distinguished at
all. */ all. */
/* Itbl support may require additional care here. FIXME!
Need to modify this to include knowledge about
user specified delays! */
if (prev_pinfo & INSN_WRITE_FPR_T) if (prev_pinfo & INSN_WRITE_FPR_T)
{ {
if (mips_optimize == 0 if (mips_optimize == 0
@ -1369,6 +1387,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
instruction may set the condition codes, and the instruction may set the condition codes, and the
current instruction uses them, we must insert two current instruction uses them, we must insert two
NOPS. */ NOPS. */
/* Itbl support may require additional care here. */
if (mips_optimize == 0 if (mips_optimize == 0
|| ((prev_pinfo & INSN_WRITE_COND_CODE) || ((prev_pinfo & INSN_WRITE_COND_CODE)
&& (pinfo & INSN_READ_COND_CODE))) && (pinfo & INSN_READ_COND_CODE)))
@ -1387,6 +1406,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
(this means it is a floating point comparison (this means it is a floating point comparison
instruction). If this instruction uses the condition instruction). If this instruction uses the condition
codes, we need to insert a single NOP. */ codes, we need to insert a single NOP. */
/* Itbl support may require additional care here. */
if (mips_optimize == 0 if (mips_optimize == 0
|| (pinfo & INSN_READ_COND_CODE)) || (pinfo & INSN_READ_COND_CODE))
++nops; ++nops;
@ -1414,6 +1434,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
/* If the previous instruction was in a noreorder section, then /* If the previous instruction was in a noreorder section, then
we don't want to insert the nop after all. */ we don't want to insert the nop after all. */
/* Itbl support may require additional care here. */
if (prev_insn_unreordered) if (prev_insn_unreordered)
nops = 0; nops = 0;
@ -1675,7 +1696,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR); mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
if (pinfo & INSN_COP) if (pinfo & INSN_COP)
{ {
/* We don't keep enough information to sort these cases out. */ /* We don't keep enough information to sort these cases out.
The itbl support does keep this information however, although
we currently don't support itbl fprmats as part of the cop
instruction. May want to add this support in the future. */
} }
/* Never set the bit for $0, which is always zero. */ /* Never set the bit for $0, which is always zero. */
mips_gprmask &=~ 1 << 0; mips_gprmask &=~ 1 << 0;
@ -1776,6 +1800,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
|| (! mips16 || (! mips16
&& mips_isa < 4 && mips_isa < 4
&& (prev_pinfo && (prev_pinfo
/* Itbl support may require additional care here. */
& (INSN_LOAD_COPROC_DELAY & (INSN_LOAD_COPROC_DELAY
| INSN_COPROC_MOVE_DELAY | INSN_COPROC_MOVE_DELAY
| INSN_WRITE_COND_CODE))) | INSN_WRITE_COND_CODE)))
@ -1787,6 +1812,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
&& mips_isa < 2 && mips_isa < 2
&& (prev_pinfo && (prev_pinfo
& (INSN_LOAD_MEMORY_DELAY & (INSN_LOAD_MEMORY_DELAY
/* Itbl support may require additional care here. */
| INSN_COPROC_MEMORY_DELAY))) | INSN_COPROC_MEMORY_DELAY)))
/* We can not swap with a branch instruction. */ /* We can not swap with a branch instruction. */
|| (prev_pinfo || (prev_pinfo
@ -1891,6 +1917,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
can not swap. */ can not swap. */
|| (! mips16 || (! mips16
&& mips_isa < 4 && mips_isa < 4
/* Itbl support may require additional care here. */
&& ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY) && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
|| (mips_isa < 2 || (mips_isa < 2
&& (prev_prev_insn.insn_mo->pinfo && (prev_prev_insn.insn_mo->pinfo
@ -2108,6 +2135,7 @@ mips_emit_delays (insns)
& (INSN_LOAD_MEMORY_DELAY & (INSN_LOAD_MEMORY_DELAY
| INSN_COPROC_MEMORY_DELAY)))) | INSN_COPROC_MEMORY_DELAY))))
{ {
/* Itbl support may require additional care here. */
++nops; ++nops;
if ((! mips16 if ((! mips16
&& mips_isa < 4 && mips_isa < 4
@ -2129,6 +2157,7 @@ mips_emit_delays (insns)
&& ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI) && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
|| (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)))) || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
{ {
/* Itbl support may require additional care here. */
if (! prev_prev_insn_unreordered) if (! prev_prev_insn_unreordered)
++nops; ++nops;
} }
@ -4421,18 +4450,22 @@ macro (ip)
goto ld; goto ld;
case M_LWC0_AB: case M_LWC0_AB:
s = "lwc0"; s = "lwc0";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ld; goto ld;
case M_LWC1_AB: case M_LWC1_AB:
s = "lwc1"; s = "lwc1";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ld; goto ld;
case M_LWC2_AB: case M_LWC2_AB:
s = "lwc2"; s = "lwc2";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ld; goto ld;
case M_LWC3_AB: case M_LWC3_AB:
s = "lwc3"; s = "lwc3";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ld; goto ld;
case M_LWL_AB: case M_LWL_AB:
@ -4445,14 +4478,17 @@ macro (ip)
goto ld; goto ld;
case M_LDC1_AB: case M_LDC1_AB:
s = "ldc1"; s = "ldc1";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ld; goto ld;
case M_LDC2_AB: case M_LDC2_AB:
s = "ldc2"; s = "ldc2";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ld; goto ld;
case M_LDC3_AB: case M_LDC3_AB:
s = "ldc3"; s = "ldc3";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ld; goto ld;
case M_LDL_AB: case M_LDL_AB:
@ -4494,18 +4530,22 @@ macro (ip)
goto st; goto st;
case M_SWC0_AB: case M_SWC0_AB:
s = "swc0"; s = "swc0";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto st; goto st;
case M_SWC1_AB: case M_SWC1_AB:
s = "swc1"; s = "swc1";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto st; goto st;
case M_SWC2_AB: case M_SWC2_AB:
s = "swc2"; s = "swc2";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto st; goto st;
case M_SWC3_AB: case M_SWC3_AB:
s = "swc3"; s = "swc3";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto st; goto st;
case M_SWL_AB: case M_SWL_AB:
@ -4523,13 +4563,16 @@ macro (ip)
case M_SDC1_AB: case M_SDC1_AB:
s = "sdc1"; s = "sdc1";
coproc = 1; coproc = 1;
/* Itbl support may require additional care here. */
goto st; goto st;
case M_SDC2_AB: case M_SDC2_AB:
s = "sdc2"; s = "sdc2";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto st; goto st;
case M_SDC3_AB: case M_SDC3_AB:
s = "sdc3"; s = "sdc3";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto st; goto st;
case M_SDL_AB: case M_SDL_AB:
@ -4541,6 +4584,7 @@ macro (ip)
tempreg = AT; tempreg = AT;
used_at = 1; used_at = 1;
ld_st: ld_st:
/* Itbl support may require additional care here. */
if (mask == M_LWC1_AB if (mask == M_LWC1_AB
|| mask == M_SWC1_AB || mask == M_SWC1_AB
|| mask == M_LDC1_AB || mask == M_LDC1_AB
@ -4939,6 +4983,7 @@ macro (ip)
* But, the resulting address is the same after relocation so why * But, the resulting address is the same after relocation so why
* generate the extra instruction? * generate the extra instruction?
*/ */
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
if (mips_isa >= 2) if (mips_isa >= 2)
{ {
@ -4959,6 +5004,7 @@ macro (ip)
s = "swc1"; s = "swc1";
fmt = "T,o(b)"; fmt = "T,o(b)";
/* Itbl support may require additional care here. */
coproc = 1; coproc = 1;
goto ldd_std; goto ldd_std;
@ -4994,6 +5040,7 @@ macro (ip)
/* Even on a big endian machine $fn comes before $fn+1. We have /* Even on a big endian machine $fn comes before $fn+1. We have
to adjust when loading from memory. We set coproc if we must to adjust when loading from memory. We set coproc if we must
load $fn+1 first. */ load $fn+1 first. */
/* Itbl support may require additional care here. */
if (! target_big_endian) if (! target_big_endian)
coproc = 0; coproc = 0;
@ -5042,6 +5089,7 @@ macro (ip)
used_at = 1; used_at = 1;
} }
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
coproc ? treg + 1 : treg, coproc ? treg + 1 : treg,
(int) BFD_RELOC_MIPS_GPREL, tempreg); (int) BFD_RELOC_MIPS_GPREL, tempreg);
@ -5051,6 +5099,7 @@ macro (ip)
undesired nop. */ undesired nop. */
hold_mips_optimize = mips_optimize; hold_mips_optimize = mips_optimize;
mips_optimize = 2; mips_optimize = 2;
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
coproc ? treg : treg + 1, coproc ? treg : treg + 1,
(int) BFD_RELOC_MIPS_GPREL, tempreg); (int) BFD_RELOC_MIPS_GPREL, tempreg);
@ -5084,6 +5133,7 @@ macro (ip)
if (p != NULL) if (p != NULL)
p += 4; p += 4;
} }
/* Itbl support may require additional care here. */
macro_build (p, &icnt, &offset_expr, s, fmt, macro_build (p, &icnt, &offset_expr, s, fmt,
coproc ? treg + 1 : treg, coproc ? treg + 1 : treg,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5091,6 +5141,7 @@ macro (ip)
p += 4; p += 4;
/* FIXME: How do we handle overflow here? */ /* FIXME: How do we handle overflow here? */
offset_expr.X_add_number += 4; offset_expr.X_add_number += 4;
/* Itbl support may require additional care here. */
macro_build (p, &icnt, &offset_expr, s, fmt, macro_build (p, &icnt, &offset_expr, s, fmt,
coproc ? treg : treg + 1, coproc ? treg : treg + 1,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5131,6 +5182,7 @@ macro (ip)
macro_build ((char *) NULL, &icnt, (expressionS *) NULL, macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
mips_isa < 3 ? "addu" : "daddu", mips_isa < 3 ? "addu" : "daddu",
"d,v,t", AT, breg, AT); "d,v,t", AT, breg, AT);
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &expr1, s, fmt, macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
coproc ? treg + 1 : treg, coproc ? treg + 1 : treg,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5140,6 +5192,7 @@ macro (ip)
nop. */ nop. */
hold_mips_optimize = mips_optimize; hold_mips_optimize = mips_optimize;
mips_optimize = 2; mips_optimize = 2;
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &expr1, s, fmt, macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
coproc ? treg : treg + 1, coproc ? treg : treg + 1,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5197,6 +5250,7 @@ macro (ip)
macro_build ((char *) NULL, &icnt, (expressionS *) NULL, macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
mips_isa < 3 ? "addu" : "daddu", mips_isa < 3 ? "addu" : "daddu",
"d,v,t", AT, breg, AT); "d,v,t", AT, breg, AT);
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &expr1, s, fmt, macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
coproc ? treg + 1 : treg, coproc ? treg + 1 : treg,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5206,6 +5260,7 @@ macro (ip)
nop. */ nop. */
hold_mips_optimize = mips_optimize; hold_mips_optimize = mips_optimize;
mips_optimize = 2; mips_optimize = 2;
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &expr1, s, fmt, macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
coproc ? treg : treg + 1, coproc ? treg : treg + 1,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5235,6 +5290,7 @@ macro (ip)
"d,v,t", AT, breg, AT); "d,v,t", AT, breg, AT);
p += 4; p += 4;
} }
/* Itbl support may require additional care here. */
macro_build (p, &icnt, &expr1, s, fmt, macro_build (p, &icnt, &expr1, s, fmt,
coproc ? treg + 1 : treg, coproc ? treg + 1 : treg,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5245,6 +5301,7 @@ macro (ip)
nop. */ nop. */
hold_mips_optimize = mips_optimize; hold_mips_optimize = mips_optimize;
mips_optimize = 2; mips_optimize = 2;
/* Itbl support may require additional care here. */
macro_build (p, &icnt, &expr1, s, fmt, macro_build (p, &icnt, &expr1, s, fmt,
coproc ? treg : treg + 1, coproc ? treg : treg + 1,
(int) BFD_RELOC_LO16, AT); (int) BFD_RELOC_LO16, AT);
@ -5274,10 +5331,12 @@ macro (ip)
used_at = 1; used_at = 1;
} }
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
coproc ? treg + 1 : treg, coproc ? treg + 1 : treg,
(int) BFD_RELOC_MIPS_GPREL, tempreg); (int) BFD_RELOC_MIPS_GPREL, tempreg);
offset_expr.X_add_number += 4; offset_expr.X_add_number += 4;
/* Itbl support may require additional care here. */
macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt, macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
coproc ? treg : treg + 1, coproc ? treg : treg + 1,
(int) BFD_RELOC_MIPS_GPREL, tempreg); (int) BFD_RELOC_MIPS_GPREL, tempreg);
@ -5303,8 +5362,59 @@ macro (ip)
macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1, macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1,
(int) BFD_RELOC_LO16, breg); (int) BFD_RELOC_LO16, breg);
return; return;
/* New code added to support COPZ instructions.
This code builds table entries out of the macros in mip_opcodes.
R4000 uses interlocks to handle coproc delays.
Other chips (like the R3000) require nops to be inserted for delays.
FIXME: Currently, we require that the user handle delays.
In order to fill delay slots for non-interlocked chips,
we must have a way to specify delays based on the coprocessor.
Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
What are the side-effects of the cop instruction?
What cache support might we have and what are its effects?
Both coprocessor & memory require delays. how long???
What registers are read/set/modified?
If an itbl is provided to interpret cop instructions,
this knowledge can be encoded in the itbl spec. */
case M_COP0:
s = "cop0";
goto copz;
case M_COP1:
s = "cop1";
goto copz;
case M_COP2:
s = "cop2";
goto copz;
case M_COP3:
s = "cop3";
copz:
/* For now we just do C (same as Cz). */
macro_build ((char *) NULL, &icnt, &offset_expr, s, "C");
return;
#ifdef LOSING_COMPILER #ifdef LOSING_COMPILER
default: default:
/* Try and see if this is a new itbl instruction.
This code builds table entries out of the macros in mip_opcodes.
FIXME: For now we just assemble the expression and pass it's
value along as a 32-bit immediate.
We may want to have the assembler assemble this value,
so that we gain the assembler's knowledge of delay slots,
symbols, etc.
Would it be more efficient to use mask (id) here? */
if (itbl_have_entries
&& immed_expr = itbl_assemble(ip->insn_mo->name, ""), immed_expr)
{
s = ip->insn_mo->name;
s2 = "cop3";
coproc = ITBL_DECODE_PNUM(immed_expr);;
macro_build ((char *) NULL, &icnt, &immed_expr, s, "C");
return;
}
macro2 (ip); macro2 (ip);
return; return;
} }
@ -5967,6 +6077,8 @@ macro2 (ip)
break; break;
default: default:
/* FIXME: Check if this is one of the itbl macros, since they are
added dynamically. */
as_bad ("Macro %s not implemented yet", ip->insn_mo->name); as_bad ("Macro %s not implemented yet", ip->insn_mo->name);
break; break;
} }
@ -6485,6 +6597,30 @@ mips_ip (str, ip)
s += 4; s += 4;
regno = KT1; regno = KT1;
} }
else if (itbl_have_entries)
{
char *p, *n;
int r;
p = s+1; /* advance past '$' */
n = itbl_get_field(&p); /* n is name */
/* See if this is a register defined in an
itbl entry */
if (r = itbl_get_reg_val(n), r)
{
/* Get_field advances to the start of the next
field, so we need to back rack to the end of
the last field. */
if (p)
s = p-1;
else
s = strchr(s,'\0');
regno = r;
}
else
goto notreg;
}
else else
goto notreg; goto notreg;
} }
@ -6508,6 +6644,9 @@ mips_ip (str, ip)
/* 'z' only matches $0. */ /* 'z' only matches $0. */
if (c == 'z' && regno != 0) if (c == 'z' && regno != 0)
break; break;
/* Now that we have assembled one operand, we use the args string
* to figure out where it goes in the instruction. */
switch (c) switch (c)
{ {
case 'r': case 'r':
@ -6540,6 +6679,11 @@ mips_ip (str, ip)
is $0. This only matches $0, and is checked is $0. This only matches $0, and is checked
outside the switch. */ outside the switch. */
break; break;
case 'D':
/* Itbl operand; not yet implemented. FIXME ?? */
break;
/* What about all other operands like 'i',
which can be specified in the opcode table? */
} }
lastregno = regno; lastregno = regno;
continue; continue;
@ -8309,6 +8453,19 @@ MIPS options:\n\
#endif #endif
} }
void
mips_init_after_args ()
{
if (itbl_have_entries)
{
/* initialize opcodes */
bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
mips_opcodes = (struct mips_opcode*) mips_builtin_opcodes;
}
}
long long
md_pcrel_from (fixP) md_pcrel_from (fixP)
fixS *fixP; fixS *fixP;

25
gas/configure vendored
View file

@ -1017,6 +1017,8 @@ EOF
;; ;;
esac esac
# additional parsers based on cpu-type
case ${cpu_type} in case ${cpu_type} in
m68k) m68k)
case ${extra_objects} in case ${extra_objects} in
@ -1024,6 +1026,27 @@ EOF
*) extra_objects="$extra_objects m68k-parse.o" ;; *) extra_objects="$extra_objects m68k-parse.o" ;;
esac esac
;; ;;
mips)
echo ${extra_objects} | grep -s "itbl-parse.o"
if test $? -ne 0 ; then
extra_objects="$extra_objects itbl-parse.o"
fi
echo ${extra_objects} | grep -s "itbl-lex.o"
if test $? -ne 0 ; then
extra_objects="$extra_objects itbl-lex.o"
fi
echo ${extra_objects} | grep -s "itbl-ops.o"
if test $? -ne 0 ; then
extra_objects="$extra_objects itbl-ops.o"
fi
;;
*)
;;
esac esac
# See if we really can support this configuration with the emulation code. # See if we really can support this configuration with the emulation code.
@ -1267,8 +1290,10 @@ EOF
files="config/tc-${target_cpu_type}.c config/tc-${target_cpu_type}.h \ files="config/tc-${target_cpu_type}.c config/tc-${target_cpu_type}.h \
config/obj-${obj_format}.h config/obj-${obj_format}.c \ config/obj-${obj_format}.h config/obj-${obj_format}.c \
config/te-${te_file}.h config/atof-${atof}.c \ config/te-${te_file}.h config/atof-${atof}.c \
config/itbl-${target_cpu_type}.h \
$extra_files" $extra_files"
links="targ-cpu.c targ-cpu.h obj-format.h obj-format.c targ-env.h atof-targ.c \ links="targ-cpu.c targ-cpu.h obj-format.h obj-format.c targ-env.h atof-targ.c \
itbl-cpu.h \
$extra_links" $extra_links"
case ${primary_bfd_gas}-${target_cpu_type}-${obj_format} in case ${primary_bfd_gas}-${target_cpu_type}-${obj_format} in

View file

@ -365,6 +365,26 @@ changequote([,])dnl
*) extra_objects="$extra_objects m68k-parse.o" ;; *) extra_objects="$extra_objects m68k-parse.o" ;;
esac esac
;; ;;
mips)
`echo ${extra_objects}` | grep -s "itbl-parse.o"
if test $? -ne 0 ; then
extra_objects="$extra_objects itbl-parse.o"
fi
`echo ${extra_objects}` | grep -s "itbl-lex.o"
if test $? -ne 0 ; then
extra_objects="$extra_objects itbl-lex.o"
fi
`echo ${extra_objects}` | grep -s "itbl-ops.o"
if test $? -ne 0 ; then
extra_objects="$extra_objects itbl-ops.o"
fi
;;
*)
;;
esac esac
# See if we really can support this configuration with the emulation code. # See if we really can support this configuration with the emulation code.
@ -527,8 +547,10 @@ AC_DEFINE_UNQUOTED(DEFAULT_EMULATION, "$DEFAULT_EMULATION")
files="config/tc-${target_cpu_type}.c config/tc-${target_cpu_type}.h \ files="config/tc-${target_cpu_type}.c config/tc-${target_cpu_type}.h \
config/obj-${obj_format}.h config/obj-${obj_format}.c \ config/obj-${obj_format}.h config/obj-${obj_format}.c \
config/te-${te_file}.h config/atof-${atof}.c \ config/te-${te_file}.h config/atof-${atof}.c \
config/itbl-${target_cpu_type}.h \
$extra_files" $extra_files"
links="targ-cpu.c targ-cpu.h obj-format.h obj-format.c targ-env.h atof-targ.c \ links="targ-cpu.c targ-cpu.h obj-format.h obj-format.c targ-env.h atof-targ.c \
itbl-cpu.h \
$extra_links" $extra_links"
case ${primary_bfd_gas}-${target_cpu_type}-${obj_format} in case ${primary_bfd_gas}-${target_cpu_type}-${obj_format} in

View file

@ -1,3 +1,25 @@
/* itbl-lex.l
Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS 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 GAS; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
%{ %{
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -42,45 +64,51 @@ HEX [0-9A-Fa-f]
return INSN; return INSN;
} }
"p"{DIGIT} { "p"{DIGIT} {
yytext[yyleng]=0; yytext[yyleng] = 0;
yylval.processor = strtoul(yytext+1,0,0); yylval.processor = strtoul (yytext+1, 0, 0);
return PNUM; return PNUM;
} }
{DIGIT}+ { {DIGIT}+ {
yytext[yyleng]=0; yytext[yyleng] = 0;
yylval.num = strtoul(yytext,0,0); yylval.num = strtoul (yytext, 0, 0);
return NUM; return NUM;
} }
"0x"{HEX}+ { "0x"{HEX}+ {
yytext[yyleng]=0; yytext[yyleng] = 0;
yylval.num = strtoul(yytext,0,0); yylval.num = strtoul (yytext, 0, 0);
return NUM; return NUM;
} }
{ALPHA}{ALNUM}* { {ALPHA}{ALNUM}* {
yytext[yyleng]=0; yytext[yyleng] = 0;
yylval.str = strdup(yytext); yylval.str = strdup (yytext);
return ID; return ID;
} }
";"|"#" { ";"|"#" {
int c; int c;
while ((c = input()) != EOF) { while ((c = input ()) != EOF)
{
if (c == '\n') if (c == '\n')
{ {
unput(c); unput (c);
break; break;
} }
} }
} }
"\n" { "\n" {
insntbl_line++; insntbl_line++;
MDBG(("in lex, NL=%d (x%x)\n",NL,NL)); MDBG (("in lex, NL = %d (x%x)\n", NL, NL));
return NL; return NL;
} }
" "|"\t" { } " "|"\t" {
}
. { . {
MDBG(("char=%x,%d\n",yytext[0],yytext[0])); MDBG (("char = %x, %d\n", yytext[0], yytext[0]));
return yytext[0]; return yytext[0];
} }
%% %%
int yywrap() { return 1; } int
yywrap ()
{
return 1;
}

View file

@ -1,4 +1,25 @@
/* itbl-ops.c
Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS 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 GAS; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
/*======================================================================*/ /*======================================================================*/
/* /*
* Herein lies the support for dynamic specification of processor * Herein lies the support for dynamic specification of processor
@ -90,15 +111,19 @@
#define min(a,b) (a<b?a:b) #define min(a,b) (a<b?a:b)
#endif #endif
int itbl_have_entries = 0;
/*======================================================================*/ /*======================================================================*/
/* structures for keeping itbl format entries */ /* structures for keeping itbl format entries */
struct itbl_range { struct itbl_range
{
int sbit; /* mask starting bit position */ int sbit; /* mask starting bit position */
int ebit; /* mask ending bit position */ int ebit; /* mask ending bit position */
}; };
struct itbl_field { struct itbl_field
{
e_type type; /* dreg/creg/greg/immed/symb */ e_type type; /* dreg/creg/greg/immed/symb */
struct itbl_range range; /* field's bitfield range within instruction */ struct itbl_range range; /* field's bitfield range within instruction */
unsigned long flags; /* field flags */ unsigned long flags; /* field flags */
@ -111,16 +136,17 @@ struct itbl_field {
* instruction where the fields are the list of operands. * instruction where the fields are the list of operands.
* The flags field below uses the same values as those defined in the * The flags field below uses the same values as those defined in the
* gnu assembler and are machine specific. */ * gnu assembler and are machine specific. */
struct itbl_entry { struct itbl_entry
{
e_processor processor; /* processor number */ e_processor processor; /* processor number */
e_type type; /* dreg/creg/greg/insn */ e_type type; /* dreg/creg/greg/insn */
char *name; /* mnemionic name for insn/register */ char *name; /* mnemionic name for insn/register */
unsigned long value; /* opcode/instruction mask/register number */ unsigned long value; /* opcode/instruction mask/register number */
unsigned long flags; /* effects of the instruction */ unsigned long flags; /* effects of the instruction */
struct itbl_range range;/* bit range within instruction for value */ struct itbl_range range; /* bit range within instruction for value */
struct itbl_field *fields; /* list of operand definitions (if any) */ struct itbl_field *fields; /* list of operand definitions (if any) */
struct itbl_entry *next; /* next entry */ struct itbl_entry *next; /* next entry */
}; };
/* local data and structures */ /* local data and structures */
@ -129,26 +155,30 @@ static int itbl_num_opcodes = 0;
/* Array of entries for each processor and entry type */ /* Array of entries for each processor and entry type */
static struct itbl_entry *entries[e_nprocs][e_ntypes] = static struct itbl_entry *entries[e_nprocs][e_ntypes] =
{ {
{ 0, 0, 0, 0, 0, 0 }, {0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0 }, {0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0 }, {0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0 } {0, 0, 0, 0, 0, 0}
}; };
/* local prototypes */ /* local prototypes */
static unsigned long build_opcode(struct itbl_entry *e); static unsigned long build_opcode PARAMS ((struct itbl_entry *e));
static e_type get_type(int yytype); static e_type get_type PARAMS ((int yytype));
static e_processor get_processor(int yyproc); static e_processor get_processor PARAMS ((int yyproc));
static struct itbl_entry **get_entries(e_processor processor, e_type type); static struct itbl_entry **get_entries PARAMS ((e_processor processor,
static struct itbl_entry *find_entry_byname(e_processor processor, e_type type, e_type type));
char *name); static struct itbl_entry *find_entry_byname PARAMS ((e_processor processor,
static struct itbl_entry *find_entry_byval(e_processor processor, e_type type, e_type type, char *name));
unsigned long val, struct itbl_range *r); static struct itbl_entry *find_entry_byval PARAMS ((e_processor processor,
static struct itbl_entry *alloc_entry(e_processor processor, e_type type, char *name, e_type type, unsigned long val, struct itbl_range *r));
unsigned long value); static struct itbl_entry *alloc_entry PARAMS ((e_processor processor,
static unsigned long apply_range(unsigned long value, struct itbl_range r); e_type type, char *name, unsigned long value));
static unsigned long extract_range(unsigned long value, struct itbl_range r); static unsigned long apply_range PARAMS ((unsigned long value,
static struct itbl_field *alloc_field(e_type type, int sbit, int ebit, unsigned long flags); struct itbl_range r));
static unsigned long extract_range PARAMS ((unsigned long value,
struct itbl_range r));
static struct itbl_field *alloc_field PARAMS ((e_type type, int sbit,
int ebit, unsigned long flags));
/*======================================================================*/ /*======================================================================*/
@ -158,31 +188,34 @@ static struct itbl_field *alloc_field(e_type type, int sbit, int ebit, unsigned
/* Open the table and use lex and yacc to parse the entries. /* Open the table and use lex and yacc to parse the entries.
* Return 1 for failure; 0 for success. */ * Return 1 for failure; 0 for success. */
int itbl_parse(char* insntbl) int
itbl_parse (char *insntbl)
{ {
extern FILE *yyin; extern FILE *yyin;
extern int yyparse(void); extern int yyparse (void);
yyin = fopen(insntbl, "r"); yyin = fopen (insntbl, "r");
if (yyin == 0) if (yyin == 0)
{ {
printf("Can't open processor instruction specification file \"%s\"\n", printf ("Can't open processor instruction specification file \"%s\"\n",
insntbl); insntbl);
return 1; return 1;
} }
else else
{ {
while (yyparse()); while (yyparse ());
} }
fclose(yyin); fclose (yyin);
itbl_have_entries = 1;
return 0; return 0;
} }
/* Add a register entry */ /* Add a register entry */
struct itbl_entry *itbl_add_reg(int yyprocessor, int yytype, char *regname, struct itbl_entry *
itbl_add_reg (int yyprocessor, int yytype, char *regname,
int regnum) int regnum)
{ {
#if 0 /* ndef STAND_ALONE */ #if 0
#include "as.h" #include "as.h"
#include "symbols.h" #include "symbols.h"
/* Since register names don't have a prefix, we put them in the symbol table so /* Since register names don't have a prefix, we put them in the symbol table so
@ -194,22 +227,23 @@ struct itbl_entry *itbl_add_reg(int yyprocessor, int yytype, char *regname,
symbol_table_insert (symbol_create (regname, reg_section, symbol_table_insert (symbol_create (regname, reg_section,
regnum, &zero_address_frag)); regnum, &zero_address_frag));
#endif #endif
return alloc_entry(get_processor(yyprocessor),get_type(yytype),regname, return alloc_entry (get_processor (yyprocessor), get_type (yytype), regname,
(unsigned long)regnum); (unsigned long) regnum);
} }
/* Add an instruction entry */ /* Add an instruction entry */
struct itbl_entry *itbl_add_insn(int yyprocessor, char *name, unsigned long value, struct itbl_entry *
itbl_add_insn (int yyprocessor, char *name, unsigned long value,
int sbit, int ebit, unsigned long flags) int sbit, int ebit, unsigned long flags)
{ {
struct itbl_entry *e; struct itbl_entry *e;
e = alloc_entry(get_processor(yyprocessor),e_insn,name,value); e = alloc_entry (get_processor (yyprocessor), e_insn, name, value);
if (e) if (e)
{ {
e->range.sbit=sbit; e->range.sbit = sbit;
e->range.ebit=ebit; e->range.ebit = ebit;
e->flags=flags; e->flags = flags;
itbl_num_opcodes++; itbl_num_opcodes++;
} }
return e; return e;
@ -217,14 +251,15 @@ struct itbl_entry *itbl_add_insn(int yyprocessor, char *name, unsigned long valu
/* Add an operand to an instruction entry */ /* Add an operand to an instruction entry */
struct itbl_field *itbl_add_operand(struct itbl_entry *e, int yytype, int sbit, struct itbl_field *
itbl_add_operand (struct itbl_entry *e, int yytype, int sbit,
int ebit, unsigned long flags) int ebit, unsigned long flags)
{ {
struct itbl_field *f, **last_f; struct itbl_field *f, **last_f;
if (!e) if (!e)
return 0; return 0;
/* Add to end of fields' list. */ /* Add to end of fields' list. */
f = alloc_field(get_type(yytype),sbit,ebit,flags); f = alloc_field (get_type (yytype), sbit, ebit, flags);
if (f) if (f)
{ {
last_f = &e->fields; last_f = &e->fields;
@ -243,10 +278,11 @@ struct itbl_field *itbl_add_operand(struct itbl_entry *e, int yytype, int sbit,
#ifndef STAND_ALONE #ifndef STAND_ALONE
#include "as.h" #include "as.h"
#include "symbols.h" #include "symbols.h"
static void append_insns_as_macros(void); static void append_insns_as_macros (void);
/* initialize for gas */ /* initialize for gas */
void itbl_init(void) void
itbl_init (void)
{ {
struct itbl_entry *e, **es; struct itbl_entry *e, **es;
e_processor procn; e_processor procn;
@ -258,78 +294,80 @@ void itbl_init(void)
/* Use symbol_create instead of symbol_new so we don't try to /* Use symbol_create instead of symbol_new so we don't try to
output registers into the object file's symbol table. */ output registers into the object file's symbol table. */
for (type=e_regtype0; type<e_nregtypes; type++) for (type = e_regtype0; type < e_nregtypes; type++)
for (procn=e_p0; procn<e_nprocs; procn++) for (procn = e_p0; procn < e_nprocs; procn++)
{ {
es = get_entries(procn, type); es = get_entries (procn, type);
for (e=*es; e; e=e->next) for (e = *es; e; e = e->next)
{ {
symbol_table_insert (symbol_create (e->name, reg_section, symbol_table_insert (symbol_create (e->name, reg_section,
e->value, &zero_address_frag)); e->value, &zero_address_frag));
} }
} }
append_insns_as_macros(); append_insns_as_macros ();
} }
/* Append insns to opcodes table and increase number of opcodes */ /* Append insns to opcodes table and increase number of opcodes
/* Structure of opcodes table: */ * Structure of opcodes table:
/* struct itbl_opcode * struct itbl_opcode
/* { * {
/* const char *name; * const char *name;
/* const char *args; /* string describing the arguments . */ * const char *args; - string describing the arguments.
/* unsigned long match; /* opcode, or ISA level if pinfo=INSN_MACRO */ * unsigned long match; - opcode, or ISA level if pinfo=INSN_MACRO
/* unsigned long mask; /* opcode mask, or macro id if pinfo=INSN_MACRO */ * unsigned long mask; - opcode mask, or macro id if pinfo=INSN_MACRO
/* unsigned long pinfo; /* insn flags, or INSN_MACRO */ * unsigned long pinfo; - insn flags, or INSN_MACRO
/* }; * };
/* examples: * examples:
* {"li", "t,i", 0x34000000, 0xffe00000, WR_t }, * {"li", "t,i", 0x34000000, 0xffe00000, WR_t },
* {"li", "t,I", 0, (int) M_LI, INSN_MACRO }, * {"li", "t,I", 0, (int) M_LI, INSN_MACRO },
*/ */
static char *form_args(struct itbl_entry *e);
static void append_insns_as_macros(void) static char *form_args (struct itbl_entry *e);
static void
append_insns_as_macros (void)
{ {
struct ITBL_OPCODE_STRUCT *new_opcodes, *o; struct ITBL_OPCODE_STRUCT *new_opcodes, *o;
struct itbl_entry *e, **es; struct itbl_entry *e, **es;
int n, id, size, new_size, new_num_opcodes; int n, id, size, new_size, new_num_opcodes;
ASSERT(itbl_num_opcodes > 0); ASSERT (itbl_num_opcodes > 0);
if (!itbl_num_opcodes) /* no new instructions to add! */ if (!itbl_num_opcodes) /* no new instructions to add! */
{ {
return; return;
} }
DBG(("previous num_opcodes=%d\n",ITBL_NUM_OPCODES)); DBG (("previous num_opcodes=%d\n", ITBL_NUM_OPCODES));
new_num_opcodes = ITBL_NUM_OPCODES + itbl_num_opcodes; new_num_opcodes = ITBL_NUM_OPCODES + itbl_num_opcodes;
ASSERT(new_num_opcodes >= itbl_num_opcodes); ASSERT (new_num_opcodes >= itbl_num_opcodes);
size = sizeof(struct ITBL_OPCODE_STRUCT) * ITBL_NUM_OPCODES; size = sizeof (struct ITBL_OPCODE_STRUCT) * ITBL_NUM_OPCODES;
ASSERT(size >= 0); ASSERT (size >= 0);
DBG(("I get=%d\n", size / sizeof(ITBL_OPCODES[0]))); DBG (("I get=%d\n", size / sizeof (ITBL_OPCODES[0])));
new_size = sizeof(struct ITBL_OPCODE_STRUCT) * new_num_opcodes; new_size = sizeof (struct ITBL_OPCODE_STRUCT) * new_num_opcodes;
ASSERT(new_size > size); ASSERT (new_size > size);
/* FIXME since ITBL_OPCODES culd be a static table, /* FIXME since ITBL_OPCODES culd be a static table,
we can't realloc or delete the old memory. */ we can't realloc or delete the old memory. */
new_opcodes = (struct ITBL_OPCODE_STRUCT*)malloc(new_size); new_opcodes = (struct ITBL_OPCODE_STRUCT *) malloc (new_size);
if (!new_opcodes) if (!new_opcodes)
{ {
printf("Unable to allocate memory for new instructions\n"); printf ("Unable to allocate memory for new instructions\n");
return; return;
} }
if (size) /* copy prexisting opcodes table */ if (size) /* copy prexisting opcodes table */
memcpy(new_opcodes, ITBL_OPCODES, size); memcpy (new_opcodes, ITBL_OPCODES, size);
/* FIXME! some NUMOPCODES are calculated expressions. /* FIXME! some NUMOPCODES are calculated expressions.
These need to be changed before itbls can be supported. */ These need to be changed before itbls can be supported. */
id = ITBL_NUM_MACROS; /* begin the next macro id after the last */ id = ITBL_NUM_MACROS; /* begin the next macro id after the last */
o = &new_opcodes[ITBL_NUM_OPCODES]; /* append macro to opcodes list */ o = &new_opcodes[ITBL_NUM_OPCODES]; /* append macro to opcodes list */
for (n=e_p0; n<e_nprocs; n++) for (n = e_p0; n < e_nprocs; n++)
{ {
es = get_entries(n,e_insn); es = get_entries (n, e_insn);
for (e=*es; e; e=e->next) for (e = *es; e; e = e->next)
{ {
/* name, args, mask, match, pinfo /* name, args, mask, match, pinfo
* {"li", "t,i", 0x34000000, 0xffe00000, WR_t }, * {"li", "t,i", 0x34000000, 0xffe00000, WR_t },
@ -337,11 +375,11 @@ static void append_insns_as_macros(void)
* Construct args from itbl_fields. * Construct args from itbl_fields.
*/ */
o->name = e->name; o->name = e->name;
o->args = strdup(form_args(e)); o->args = strdup (form_args (e));
o->mask = apply_range(e->value,e->range); o->mask = apply_range (e->value, e->range);
/* FIXME how to catch durring assembly? */ /* FIXME how to catch durring assembly? */
/* mask to identify this insn */ /* mask to identify this insn */
o->match = apply_range(e->value,e->range); o->match = apply_range (e->value, e->range);
o->pinfo = 0; o->pinfo = 0;
#ifdef USE_MACROS #ifdef USE_MACROS
@ -366,36 +404,49 @@ static void append_insns_as_macros(void)
Don't free name though, since name is being used by the new Don't free name though, since name is being used by the new
opcodes table. opcodes table.
Eventually, we should also free the new opcodes table itself on exit. Eventually, we should also free the new opcodes table itself
on exit.
*/ */
} }
static char *form_args(struct itbl_entry *e) static char *
form_args (struct itbl_entry *e)
{ {
static char s[31]; static char s[31];
char c=0, *p=s; char c = 0, *p = s;
struct itbl_field *f; struct itbl_field *f;
ASSERT(e); ASSERT (e);
for (f=e->fields; f; f=f->next) for (f = e->fields; f; f = f->next)
{ {
switch (f->type) switch (f->type)
{ {
case e_dreg: c='d'; break; case e_dreg:
case e_creg: c='t'; break; c = 'd';
case e_greg: c='s'; break; break;
case e_immed: c='i'; break; case e_creg:
case e_addr: c='a'; break; c = 't';
default: c=0; /* ignore; unknown field type */ break;
case e_greg:
c = 's';
break;
case e_immed:
c = 'i';
break;
case e_addr:
c = 'a';
break;
default:
c = 0; /* ignore; unknown field type */
} }
if (c) if (c)
{ {
if (p!=s) if (p != s)
*p++=','; *p++ = ',';
*p++=c; *p++ = c;
} }
} }
*p=0; *p = 0;
return s; return s;
} }
#endif /* !STAND_ALONE */ #endif /* !STAND_ALONE */
@ -403,25 +454,27 @@ static char *form_args(struct itbl_entry *e)
/* Get processor's register name from val */ /* Get processor's register name from val */
unsigned long itbl_get_reg_val(char *name) unsigned long
itbl_get_reg_val (char *name)
{ {
e_type t; e_type t;
e_processor p; e_processor p;
int r=0; int r = 0;
for (p=e_p0; p<e_nprocs; p++) for (p = e_p0; p < e_nprocs; p++)
for (t=e_regtype0; t<e_nregtypes; t++) for (t = e_regtype0; t < e_nregtypes; t++)
{ {
if (r = itbl_get_val(p, t, name), r) if (r = itbl_get_val (p, t, name), r)
return r; return r;
} }
return 0; return 0;
} }
char *itbl_get_name(e_processor processor, e_type type, unsigned long val) char *
itbl_get_name (e_processor processor, e_type type, unsigned long val)
{ {
struct itbl_entry *r; struct itbl_entry *r;
/* type depends on instruction passed */ /* type depends on instruction passed */
r = find_entry_byval(processor,type,val,0); r = find_entry_byval (processor, type, val, 0);
if (r) if (r)
return r->name; return r->name;
else else
@ -430,11 +483,12 @@ char *itbl_get_name(e_processor processor, e_type type, unsigned long val)
/* Get processor's register value from name */ /* Get processor's register value from name */
unsigned long itbl_get_val(e_processor processor, e_type type, char *name) unsigned long
itbl_get_val (e_processor processor, e_type type, char *name)
{ {
struct itbl_entry *r; struct itbl_entry *r;
/* type depends on instruction passed */ /* type depends on instruction passed */
r = find_entry_byname(processor,type,name); r = find_entry_byname (processor, type, name);
if (r) if (r)
return r->value; return r->value;
else else
@ -447,7 +501,8 @@ unsigned long itbl_get_val(e_processor processor, e_type type, char *name)
* s - operands * s - operands
* returns - long word for assembled instruction */ * returns - long word for assembled instruction */
unsigned long itbl_assemble(char *name, char *s) unsigned long
itbl_assemble (char *name, char *s)
{ {
unsigned long opcode; unsigned long opcode;
struct itbl_entry *e; struct itbl_entry *e;
@ -459,23 +514,24 @@ unsigned long itbl_assemble(char *name, char *s)
return 0; /* error! must have a opcode name/expr */ return 0; /* error! must have a opcode name/expr */
/* find entry in list of instructions for all processors */ /* find entry in list of instructions for all processors */
for (processor=0; processor<e_nprocs; processor++) for (processor = 0; processor < e_nprocs; processor++)
{ {
e = find_entry_byname(processor, e_insn, name); e = find_entry_byname (processor, e_insn, name);
if (e) break; if (e)
break;
} }
if (!e) if (!e)
return 0; /* opcode not in table; invalid instrustion */ return 0; /* opcode not in table; invalid instrustion */
opcode = build_opcode(e); opcode = build_opcode (e);
/* parse opcode's args (if any) */ /* parse opcode's args (if any) */
for (f=e->fields; f; f=f->next) /* for each arg, ... */ for (f = e->fields; f; f = f->next) /* for each arg, ... */
{ {
struct itbl_entry *r; struct itbl_entry *r;
unsigned long value; unsigned long value;
if (!s || !*s) if (!s || !*s)
return 0; /* error - not enough operands */ return 0; /* error - not enough operands */
n = itbl_get_field(&s); n = itbl_get_field (&s);
/* n should be in form $n or 0xhhh (are symbol names valid?? */ /* n should be in form $n or 0xhhh (are symbol names valid?? */
switch (f->type) switch (f->type)
{ {
@ -487,14 +543,14 @@ unsigned long itbl_assemble(char *name, char *s)
if (*n == '$') if (*n == '$')
{ {
n++; n++;
value = strtol(n,0,10); value = strtol (n, 0, 10);
/* FIXME! could have "0l"... then what?? */ /* FIXME! could have "0l"... then what?? */
if (value == 0 && *n!='0') if (value == 0 && *n != '0')
return 0; /* error; invalid operand */ return 0; /* error; invalid operand */
} }
else else
{ {
r = find_entry_byname(e->processor,f->type,n); r = find_entry_byname (e->processor, f->type, n);
if (r) if (r)
value = r->value; value = r->value;
else else
@ -511,24 +567,24 @@ unsigned long itbl_assemble(char *name, char *s)
*/ */
/* If not a symbol, fall thru to IMMED */ /* If not a symbol, fall thru to IMMED */
case e_immed: case e_immed:
if (*n=='0' && *(n+1)=='x') /* hex begins 0x... */ if (*n == '0' && *(n + 1) == 'x') /* hex begins 0x... */
{ {
n+=2; n += 2;
value = strtol(n,0,16); value = strtol (n, 0, 16);
/* FIXME! could have "0xl"... then what?? */ /* FIXME! could have "0xl"... then what?? */
} }
else else
{ {
value = strtol(n,0,10); value = strtol (n, 0, 10);
/* FIXME! could have "0l"... then what?? */ /* FIXME! could have "0l"... then what?? */
if (value == 0 && *n!='0') if (value == 0 && *n != '0')
return 0; /* error; invalid operand */ return 0; /* error; invalid operand */
} }
break; break;
default: default:
return 0; /* error; invalid field spec */ return 0; /* error; invalid field spec */
} }
opcode |= apply_range(value,f->range); opcode |= apply_range (value, f->range);
} }
if (s && *s) if (s && *s)
return 0; /* error - too many operands */ return 0; /* error - too many operands */
@ -541,33 +597,34 @@ unsigned long itbl_assemble(char *name, char *s)
* returns - 1 if succeeded; 0 if failed * returns - 1 if succeeded; 0 if failed
*/ */
int itbl_disassemble(char *s, unsigned long insn) int
itbl_disassemble (char *s, unsigned long insn)
{ {
e_processor processor; e_processor processor;
struct itbl_entry *e; struct itbl_entry *e;
struct itbl_field *f; struct itbl_field *f;
if (!ITBL_IS_INSN(insn)) if (!ITBL_IS_INSN (insn))
return 0; /* error*/ return 0; /* error*/
processor = get_processor(ITBL_DECODE_PNUM(insn)); processor = get_processor (ITBL_DECODE_PNUM (insn));
/* find entry in list */ /* find entry in list */
e = find_entry_byval(processor, e_insn, insn, 0); e = find_entry_byval (processor, e_insn, insn, 0);
if (!e) if (!e)
return 0; /* opcode not in table; invalid instrustion */ return 0; /* opcode not in table; invalid instrustion */
strcpy(s, e->name); strcpy (s, e->name);
/* parse insn's args (if any) */ /* parse insn's args (if any) */
for (f=e->fields; f; f=f->next) /* for each arg, ... */ for (f = e->fields; f; f = f->next) /* for each arg, ... */
{ {
struct itbl_entry *r; struct itbl_entry *r;
unsigned long value; unsigned long value;
if (f==e->fields) /* first operand is preceeded by tab */ if (f == e->fields) /* first operand is preceeded by tab */
strcat(s,"\t"); strcat (s, "\t");
else /* ','s separate following operands */ else /* ','s separate following operands */
strcat(s,","); strcat (s, ",");
value = extract_range(insn, f->range); value = extract_range (insn, f->range);
/* n should be in form $n or 0xhhh (are symbol names valid?? */ /* n should be in form $n or 0xhhh (are symbol names valid?? */
switch (f->type) switch (f->type)
{ {
@ -576,11 +633,11 @@ int itbl_disassemble(char *s, unsigned long insn)
case e_greg: case e_greg:
/* Accept either a string name /* Accept either a string name
* or '$' followed by the register number */ * or '$' followed by the register number */
r = find_entry_byval(e->processor,f->type,value,&f->range); r = find_entry_byval (e->processor, f->type, value, &f->range);
if (r) if (r)
strcat(s,r->name); strcat (s, r->name);
else else
sprintf(s,"%s$%d",s,value); sprintf (s, "%s$%d", s, value);
break; break;
case e_addr: case e_addr:
/* use assembler's symbol table to find symbol */ /* use assembler's symbol table to find symbol */
@ -589,7 +646,7 @@ int itbl_disassemble(char *s, unsigned long insn)
*/ */
/* If not a symbol, fall thru to IMMED */ /* If not a symbol, fall thru to IMMED */
case e_immed: case e_immed:
sprintf(s,"%s0x%x",s,value); sprintf (s, "%s0x%x", s, value);
break; break;
default: default:
return 0; /* error; invalid field spec */ return 0; /* error; invalid field spec */
@ -607,12 +664,13 @@ int itbl_disassemble(char *s, unsigned long insn)
/* Calculate instruction's opcode and function values from entry */ /* Calculate instruction's opcode and function values from entry */
static unsigned long build_opcode(struct itbl_entry *e) static unsigned long
build_opcode (struct itbl_entry *e)
{ {
unsigned long opcode; unsigned long opcode;
opcode = apply_range(e->value,e->range); opcode = apply_range (e->value, e->range);
opcode |= ITBL_ENCODE_PNUM(e->processor); opcode |= ITBL_ENCODE_PNUM (e->processor);
return opcode; return opcode;
} }
@ -627,15 +685,16 @@ static unsigned long build_opcode(struct itbl_entry *e)
* mask: 0x01f00000. * mask: 0x01f00000.
*/ */
static unsigned long apply_range(unsigned long rval, struct itbl_range r) static unsigned long
apply_range (unsigned long rval, struct itbl_range r)
{ {
unsigned long mask; unsigned long mask;
unsigned long aval; unsigned long aval;
int len = MAX_BITPOS - r.sbit; int len = MAX_BITPOS - r.sbit;
ASSERT(r.sbit >= r.ebit); ASSERT (r.sbit >= r.ebit);
ASSERT(MAX_BITPOS >= r.sbit); ASSERT (MAX_BITPOS >= r.sbit);
ASSERT(r.ebit >= 0); ASSERT (r.ebit >= 0);
/* create mask by truncating 1s by shifting */ /* create mask by truncating 1s by shifting */
mask = 0xffffffff << len; mask = 0xffffffff << len;
@ -650,7 +709,8 @@ static unsigned long apply_range(unsigned long rval, struct itbl_range r)
/* Calculate relative value given the absolute value and bit position range /* Calculate relative value given the absolute value and bit position range
* within the instruction. */ * within the instruction. */
static unsigned long extract_range(unsigned long aval, struct itbl_range r) static unsigned long
extract_range (unsigned long aval, struct itbl_range r)
{ {
unsigned long mask; unsigned long mask;
unsigned long rval; unsigned long rval;
@ -671,7 +731,8 @@ static unsigned long extract_range(unsigned long aval, struct itbl_range r)
/* Return next argument from string pointer "s" and advance s. /* Return next argument from string pointer "s" and advance s.
* delimiters are " ,\0" */ * delimiters are " ,\0" */
char *itbl_get_field(char **S) char *
itbl_get_field (char **S)
{ {
static char n[128]; static char n[128];
char *p, *ps, *s; char *p, *ps, *s;
@ -680,18 +741,23 @@ char *itbl_get_field(char **S)
s = *S; s = *S;
if (!s || !*s) if (!s || !*s)
return 0; return 0;
p = s+strlen(s); p = s + strlen (s);
if (ps=strchr(s,','),ps) p = ps; if (ps = strchr (s, ','), ps)
if (ps=strchr(s,' '),ps) p = min(p,ps); p = ps;
if (ps=strchr(s,'\0'),ps) p = min(p,ps); if (ps = strchr (s, ' '), ps)
if (p==0) p = min (p, ps);
if (ps = strchr (s, '\0'), ps)
p = min (p, ps);
if (p == 0)
return 0; /* error! */ return 0; /* error! */
len = p-s; len = p - s;
ASSERT(128>len+1); ASSERT (128 > len + 1);
strncpy(n,s,len); strncpy (n, s, len);
n[len]=0; n[len] = 0;
if (s[len]=='\0') s=0; /* no more args */ if (s[len] == '\0')
else s+=len+1; /* advance to next arg */ s = 0; /* no more args */
else
s += len + 1; /* advance to next arg */
*S = s; *S = s;
return n; return n;
@ -701,15 +767,16 @@ char *itbl_get_field(char **S)
* to find one matching the name "n". * to find one matching the name "n".
* Return a pointer to the entry */ * Return a pointer to the entry */
static struct itbl_entry *find_entry_byname(e_processor processor, static struct itbl_entry *
find_entry_byname (e_processor processor,
e_type type, char *n) e_type type, char *n)
{ {
struct itbl_entry *e, **es; struct itbl_entry *e, **es;
es = get_entries(processor, type); es = get_entries (processor, type);
for (e=*es; e; e=e->next) /* for each entry, ... */ for (e = *es; e; e = e->next) /* for each entry, ... */
{ {
if (!strcmp(e->name,n)) if (!strcmp (e->name, n))
return e; return e;
} }
return 0; return 0;
@ -721,14 +788,15 @@ static struct itbl_entry *find_entry_byname(e_processor processor,
* This function is used for disassembling fields of an instruction. * This function is used for disassembling fields of an instruction.
*/ */
static struct itbl_entry *find_entry_byval(e_processor processor, e_type type, static struct itbl_entry *
find_entry_byval (e_processor processor, e_type type,
unsigned long val, struct itbl_range *r) unsigned long val, struct itbl_range *r)
{ {
struct itbl_entry *e, **es; struct itbl_entry *e, **es;
unsigned long eval; unsigned long eval;
es = get_entries(processor, type); es = get_entries (processor, type);
for (e=*es; e; e=e->next) /* for each entry, ... */ for (e = *es; e; e = e->next) /* for each entry, ... */
{ {
if (processor != e->processor) if (processor != e->processor)
continue; continue;
@ -739,21 +807,21 @@ static struct itbl_entry *find_entry_byval(e_processor processor, e_type type,
* For operands, we get an extracted value and a range. * For operands, we get an extracted value and a range.
*/ */
/* if range is 0, mask val against the range of the compared entry. */ /* if range is 0, mask val against the range of the compared entry. */
if (r==0) /* if no range passed, must be whole 32-bits if (r == 0) /* if no range passed, must be whole 32-bits
* so create 32-bit value from entry's range */ * so create 32-bit value from entry's range */
{ {
eval = apply_range(e->value,e->range); eval = apply_range (e->value, e->range);
val &= apply_range(0xffffffff,e->range); val &= apply_range (0xffffffff, e->range);
} }
else if (r->sbit == e->range.sbit && r->ebit == e->range.ebit else if (r->sbit == e->range.sbit && r->ebit == e->range.ebit
|| e->range.sbit == 0 && e->range.ebit == 0) || e->range.sbit == 0 && e->range.ebit == 0)
{ {
eval = apply_range(e->value,*r); eval = apply_range (e->value, *r);
val = apply_range(val, *r); val = apply_range (val, *r);
} }
else else
continue; continue;
if (val==eval) if (val == eval)
return e; return e;
} }
return 0; return 0;
@ -761,14 +829,16 @@ static struct itbl_entry *find_entry_byval(e_processor processor, e_type type,
/* Return a pointer to the list of entries for a given processor and type. */ /* Return a pointer to the list of entries for a given processor and type. */
static struct itbl_entry **get_entries(e_processor processor, e_type type) static struct itbl_entry **
get_entries (e_processor processor, e_type type)
{ {
return &entries[processor][type]; return &entries[processor][type];
} }
/* Return an integral value for the processor passed from yyparse. */ /* Return an integral value for the processor passed from yyparse. */
static e_processor get_processor(int yyproc) static e_processor
get_processor (int yyproc)
{ {
/* translate from yacc's processor to enum */ /* translate from yacc's processor to enum */
if (yyproc >= e_p0 && yyproc < e_nprocs) if (yyproc >= e_p0 && yyproc < e_nprocs)
@ -778,17 +848,24 @@ static e_processor get_processor(int yyproc)
/* Return an integral value for the entry type passed from yyparse. */ /* Return an integral value for the entry type passed from yyparse. */
static e_type get_type(int yytype) static e_type
get_type (int yytype)
{ {
switch(yytype) switch (yytype)
{ {
/* translate from yacc's type to enum */ /* translate from yacc's type to enum */
case INSN: return e_insn; case INSN:
case DREG: return e_dreg; return e_insn;
case CREG: return e_creg; case DREG:
case GREG: return e_greg; return e_dreg;
case ADDR: return e_addr; case CREG:
case IMMED: return e_immed; return e_creg;
case GREG:
return e_greg;
case ADDR:
return e_addr;
case IMMED:
return e_immed;
default: default:
return e_invtype; /* error; invalid type */ return e_invtype; /* error; invalid type */
} }
@ -797,21 +874,24 @@ static e_type get_type(int yytype)
/* Allocate and initialize an entry */ /* Allocate and initialize an entry */
static struct itbl_entry *alloc_entry(e_processor processor, e_type type, static struct itbl_entry *
alloc_entry (e_processor processor, e_type type,
char *name, unsigned long value) char *name, unsigned long value)
{ {
struct itbl_entry *e, **es; struct itbl_entry *e, **es;
if (!name) return 0; if (!name)
e = (struct itbl_entry*) malloc(sizeof(struct itbl_entry)); return 0;
e = (struct itbl_entry *) malloc (sizeof (struct itbl_entry));
if (e) if (e)
{ {
memset(e,0,sizeof(struct itbl_entry)); memset (e, 0, sizeof (struct itbl_entry));
e->name = (char *) malloc(sizeof(strlen(name))+1); e->name = (char *) malloc (sizeof (strlen (name)) + 1);
if (e->name) strcpy(e->name,name); if (e->name)
strcpy (e->name, name);
e->processor = processor; e->processor = processor;
e->type = type; e->type = type;
e->value = value; e->value = value;
es = get_entries(e->processor,e->type); es = get_entries (e->processor, e->type);
e->next = *es; e->next = *es;
*es = e; *es = e;
} }
@ -820,14 +900,15 @@ static struct itbl_entry *alloc_entry(e_processor processor, e_type type,
/* Allocate and initialize an entry's field */ /* Allocate and initialize an entry's field */
static struct itbl_field *alloc_field(e_type type, int sbit, int ebit, static struct itbl_field *
alloc_field (e_type type, int sbit, int ebit,
unsigned long flags) unsigned long flags)
{ {
struct itbl_field *f; struct itbl_field *f;
f = (struct itbl_field*) malloc(sizeof(struct itbl_field)); f = (struct itbl_field *) malloc (sizeof (struct itbl_field));
if (f) if (f)
{ {
memset(f,0,sizeof(struct itbl_field)); memset (f, 0, sizeof (struct itbl_field));
f->type = type; f->type = type;
f->range.sbit = sbit; f->range.sbit = sbit;
f->range.ebit = ebit; f->range.ebit = ebit;
@ -835,4 +916,3 @@ static struct itbl_field *alloc_field(e_type type, int sbit, int ebit,
} }
return f; return f;
} }

View file

@ -1,6 +1,28 @@
/* itbl-ops.h
Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS 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 GAS; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
/* External functions, constants and defines for itbl support */ /* External functions, constants and defines for itbl support */
#include "ansidecl.h"
#include "itbl-cpu.h" #include "itbl-cpu.h"
/* Defaults for definitions required by generic code */ /* Defaults for definitions required by generic code */
@ -32,44 +54,49 @@ typedef ITBL_TYPE t_insn;
/* types of entries */ /* types of entries */
typedef enum typedef enum
{ {
e_insn, e_insn,
e_dreg, e_dreg,
e_regtype0 = e_dreg, e_regtype0 = e_dreg,
e_creg, e_creg,
e_greg, e_greg,
e_addr, e_addr,
e_nregtypes = e_greg+1, e_nregtypes = e_greg + 1,
e_immed, e_immed,
e_ntypes, e_ntypes,
e_invtype /* invalid type */ e_invtype /* invalid type */
} e_type; } e_type;
typedef enum typedef enum
{ {
e_p0, e_p0,
e_nprocs=NUMBER_OF_PROCESSORS, e_nprocs = NUMBER_OF_PROCESSORS,
e_invproc /* invalid processor */ e_invproc /* invalid processor */
} e_processor; } e_processor;
/* 0 means an instruction table was not specified. */
extern int itbl_have_entries;
/* These routines are visible to the main part of the assembler */ /* These routines are visible to the main part of the assembler */
int itbl_parse(char* insntbl); int itbl_parse PARAMS ((char *insntbl));
void itbl_init(void); void itbl_init PARAMS ((void));
char *itbl_get_field(char **s); char *itbl_get_field PARAMS ((char **s));
unsigned long itbl_assemble(char *name, char *operands); unsigned long itbl_assemble PARAMS ((char *name, char *operands));
int itbl_disassemble(char *str, unsigned long insn); int itbl_disassemble PARAMS ((char *str, unsigned long insn));
int itbl_parse(char *tbl); /* parses insn tbl */ int itbl_parse PARAMS ((char *tbl)); /* parses insn tbl */
unsigned long itbl_get_reg_val(char *name); unsigned long itbl_get_reg_val PARAMS ((char *name));
unsigned long itbl_get_val(e_processor processor, e_type type, char *name); unsigned long itbl_get_val PARAMS ((e_processor processor, e_type type,
char *itbl_get_name(e_processor processor, e_type type, unsigned long val); char *name));
char *itbl_get_name PARAMS ((e_processor processor, e_type type,
unsigned long val));
/* These routines are called by the table parser used to build the /* These routines are called by the table parser used to build the
* dynamic list of new processor instructions and registers. */ dynamic list of new processor instructions and registers. */
struct itbl_entry *itbl_add_reg(int yyproc, int yytype, char *regname, int regnum);
struct itbl_entry *itbl_add_insn(int yyproc, char *name, unsigned long value,
int sbit, int ebit, unsigned long flags);
struct itbl_field *itbl_add_operand(struct itbl_entry *e, int yytype,
int sbit, int ebit, unsigned long flags);
struct itbl_entry *itbl_add_reg PARAMS ((int yyproc, int yytype,
char *regname, int regnum));
struct itbl_entry *itbl_add_insn PARAMS ((int yyproc, char *name,
unsigned long value, int sbit, int ebit, unsigned long flags));
struct itbl_field *itbl_add_operand PARAMS ((struct itbl_entry * e, int yytype,
int sbit, int ebit, unsigned long flags));

View file

@ -1,4 +1,27 @@
/* itbl-parse.y
Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS 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 GAS; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
%{ %{
/* /*
Yacc grammar for instruction table entries. Yacc grammar for instruction table entries.
@ -48,26 +71,33 @@ by the assembler, and are listed here only for reference.
Class format instructions Class format instructions
------------------------------------------------- -------------------------------------------------
Class1: op base rt offset Class1:
LWCz rt,offset(base) op base rt offset
SWCz rt,offset(base) LWCz rt,offset (base)
Class2: COPz sub rt rd 0 SWCz rt,offset (base)
Class2:
COPz sub rt rd 0
MTCz rt,rd MTCz rt,rd
MFCz rt,rd MFCz rt,rd
CTCz rt,rd CTCz rt,rd
CFCz rt,rd CFCz rt,rd
Class3: COPz CO cofun Class3:
COPz CO cofun
COPz cofun COPz cofun
Class4: COPz BC br offset Class4:
COPz BC br offset
BCzT offset BCzT offset
BCzF offset BCzF offset
Class5: COPz sub rt rd 0 Class5:
COPz sub rt rd 0
DMFCz rt,rd DMFCz rt,rd
DMTCz rt,rd DMTCz rt,rd
Class6: op base rt offset Class6:
LDCz rt,offset(base) op base rt offset
SDCz rt,offset(base) LDCz rt,offset (base)
Class7: COPz BC br offset SDCz rt,offset (base)
Class7:
COPz BC br offset
BCzTL offset BCzTL offset
BCzFL offset BCzFL offset
@ -141,7 +171,7 @@ The table:
will allow the assembler to accept the following coprocessor instructions: will allow the assembler to accept the following coprocessor instructions:
LWC1 d1,0x100($2) LWC1 d1,0x100 ($2)
fill fill
Here, the general purpose register "$2", and instruction "LWC1", are standard Here, the general purpose register "$2", and instruction "LWC1", are standard
@ -205,7 +235,8 @@ use 0s to mask out the ranges which don't apply.
May decide to modify the syntax to allow commas separate multiple May decide to modify the syntax to allow commas separate multiple
ranges within an instruction (range','range). ranges within an instruction (range','range).
Changes in grammar: The number of parms argument to the function entry Changes in grammar:
The number of parms argument to the function entry
was deleted from the original format such that we now count the fields. was deleted from the original format such that we now count the fields.
---- ----
@ -245,12 +276,13 @@ FIXME! hex is ambiguous with any digit
static int sbit, ebit; static int sbit, ebit;
static struct itbl_entry *insn=0; static struct itbl_entry *insn=0;
extern int insntbl_line; extern int insntbl_line;
int yyparse(void); int yyparse (void);
int yylex(void); int yylex (void);
%} %}
%union { %union
{
char *str; char *str;
int num; int num;
int processor; int processor;
@ -266,62 +298,69 @@ int yylex(void);
%% %%
insntbl: entrys insntbl:
entrys
; ;
entrys: entry entrys entrys:
entry entrys
| |
; ;
entry: pnum regtype name value NL entry:
pnum regtype name value NL
{ {
DBG(("line %d: entry pnum=%d type=%d name=%s value=x%x\n", DBG (("line %d: entry pnum=%d type=%d name=%s value=x%x\n",
insntbl_line, $1, $2, $3, $4)); insntbl_line, $1, $2, $3, $4));
itbl_add_reg($1, $2, $3, $4); itbl_add_reg ($1, $2, $3, $4);
} }
| pnum INSN name value range flags | pnum INSN name value range flags
{ {
DBG(("line %d: entry pnum=%d type=INSN name=%s value=x%x", DBG (("line %d: entry pnum=%d type=INSN name=%s value=x%x",
insntbl_line, $1, $3, $4)); insntbl_line, $1, $3, $4));
DBG((" sbit=%d ebit=%d flags=0x%x\n", sbit, ebit, $6)); DBG ((" sbit=%d ebit=%d flags=0x%x\n", sbit, ebit, $6));
insn=itbl_add_insn($1, $3, $4, sbit, ebit, $6); insn=itbl_add_insn ($1, $3, $4, sbit, ebit, $6);
} }
fieldspecs NL fieldspecs NL
| NL | NL
| error NL | error NL
; ;
fieldspecs: ',' fieldspec fieldspecs fieldspecs:
',' fieldspec fieldspecs
| fieldspec fieldspecs | fieldspec fieldspecs
| |
; ;
ftype: regtype ftype:
regtype
{ {
DBGL2(("ftype\n")); DBGL2 (("ftype\n"));
$$ = $1; $$ = $1;
} }
| ADDR | ADDR
{ {
DBGL2(("addr\n")); DBGL2 (("addr\n"));
$$ = ADDR; $$ = ADDR;
} }
| IMMED | IMMED
{ {
DBGL2(("immed\n")); DBGL2 (("immed\n"));
$$ = IMMED; $$ = IMMED;
} }
; ;
fieldspec: ftype range flags fieldspec:
ftype range flags
{ {
DBG(("line %d: field type=%d sbit=%d ebit=%d, flags=0x%x\n", DBG (("line %d: field type=%d sbit=%d ebit=%d, flags=0x%x\n",
insntbl_line, $1, sbit, ebit, $3)); insntbl_line, $1, sbit, ebit, $3));
itbl_add_operand(insn, $1, sbit, ebit, $3); itbl_add_operand (insn, $1, sbit, ebit, $3);
} }
; ;
flagexpr: NUM '|' flagexpr flagexpr:
NUM '|' flagexpr
{ {
$$ = $1 | $3; $$ = $1 | $3;
} }
@ -335,9 +374,10 @@ flagexpr: NUM '|' flagexpr
} }
; ;
flags: '*' flagexpr flags:
'*' flagexpr
{ {
DBGL2(("flags=%d\n", $2)); DBGL2 (("flags=%d\n", $2));
$$ = $2; $$ = $2;
} }
| |
@ -346,9 +386,10 @@ flags: '*' flagexpr
} }
; ;
range: ':' NUM '-' NUM range:
':' NUM '-' NUM
{ {
DBGL2(("range %d %d\n", $2, $4)); DBGL2 (("range %d %d\n", $2, $4));
sbit = $2; sbit = $2;
ebit = $4; ebit = $4;
} }
@ -359,55 +400,60 @@ range: ':' NUM '-' NUM
} }
; ;
pnum: PNUM pnum:
PNUM
{ {
DBGL2(("pnum=%d\n",$1)); DBGL2 (("pnum=%d\n",$1));
$$ = $1; $$ = $1;
} }
; ;
regtype: DREG regtype:
DREG
{ {
DBGL2(("dreg\n")); DBGL2 (("dreg\n"));
$$ = DREG; $$ = DREG;
} }
| CREG | CREG
{ {
DBGL2(("creg\n")); DBGL2 (("creg\n"));
$$ = CREG; $$ = CREG;
} }
| GREG | GREG
{ {
DBGL2(("greg\n")); DBGL2 (("greg\n"));
$$ = GREG; $$ = GREG;
} }
; ;
name: ID name:
ID
{ {
DBGL2(("name=%s\n",$1)); DBGL2 (("name=%s\n",$1));
$$ = $1; $$ = $1;
} }
; ;
number: NUM number:
NUM
{ {
DBGL2(("num=%d\n",$1)); DBGL2 (("num=%d\n",$1));
$$ = $1; $$ = $1;
} }
; ;
value: NUM value:
NUM
{ {
DBGL2(("val=x%x\n",$1)); DBGL2 (("val=x%x\n",$1));
$$ = $1; $$ = $1;
} }
; ;
%% %%
void yyerror(char *msg) void
yyerror (char *msg)
{ {
printf("line %d: %s\n", insntbl_line, msg); printf ("line %d: %s\n", insntbl_line, msg);
} }