diff options
author | Nick Clifton <nickc@redhat.com> | 2004-11-08 13:17:43 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2004-11-08 13:17:43 +0000 |
commit | 7499d566bb9495cac23109f01ded20f8849d08ae (patch) | |
tree | 30530048723553d2c770364cea592becdda630c1 /gas/config | |
parent | dc85a459cf260d72565cb988960c12ff9cd3da3c (diff) | |
download | gdb-7499d566bb9495cac23109f01ded20f8849d08ae.zip gdb-7499d566bb9495cac23109f01ded20f8849d08ae.tar.gz gdb-7499d566bb9495cac23109f01ded20f8849d08ae.tar.bz2 |
Add support fpr MAXQ processor
Diffstat (limited to 'gas/config')
-rw-r--r-- | gas/config/obj-coff.h | 5 | ||||
-rw-r--r-- | gas/config/tc-maxq.c | 3185 | ||||
-rw-r--r-- | gas/config/tc-maxq.h | 172 |
3 files changed, 3362 insertions, 0 deletions
diff --git a/gas/config/obj-coff.h b/gas/config/obj-coff.h index 90d7e99..e010a39 100644 --- a/gas/config/obj-coff.h +++ b/gas/config/obj-coff.h @@ -115,6 +115,11 @@ #define TARGET_FORMAT "coff-h8500" #endif +#ifdef TC_MAXQ20 +#include "coff/maxq.h" +#define TARGET_FORMAT "coff-maxq" +#endif + #ifdef TC_SH #ifdef TE_PE diff --git a/gas/config/tc-maxq.c b/gas/config/tc-maxq.c new file mode 100644 index 0000000..e467e04 --- /dev/null +++ b/gas/config/tc-maxq.c @@ -0,0 +1,3185 @@ +/* tc-maxq.c -- assembler code for a MAXQ chip. + + Copyright 2004 Free Software Foundation, Inc. + + Contributed by HCL Technologies Pvt. Ltd. + + Author: Vineet Sharma(vineets@noida.hcltech.com) Inderpreet + S.(inderpreetb@noida.hcltech.com) + + This file is part of GAS. + + 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 "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "dwarf2dbg.h" +#include "tc-maxq.h" +#include "opcode/maxq.h" +#include "ctype.h" + +#ifndef MAXQ10S +#define MAXQ10S 1 +#endif + +#ifndef _STRING_H +#include "string.h" +#endif + +#ifndef DEFAULT_ARCH +#define DEFAULT_ARCH "MAXQ20" +#endif + +#ifndef MAX_OPERANDS +#define MAX_OPERANDS 2 +#endif + +#ifndef MAX_MNEM_SIZE +#define MAX_MNEM_SIZE 8 +#endif + +#ifndef END_OF_INSN +#define END_OF_INSN '\0' +#endif + +#ifndef IMMEDIATE_PREFIX +#define IMMEDIATE_PREFIX '#' +#endif + +#ifndef MAX_REG_NAME_SIZE +#define MAX_REG_NAME_SIZE 4 +#endif + +#ifndef MAX_MEM_NAME_SIZE +#define MAX_MEM_NAME_SIZE 9 +#endif + +/* opcode for PFX[0]. */ +#define PFX0 0x0b + +/* Set default to MAXQ20. */ +unsigned int max_version = 20; + +const char *default_arch = DEFAULT_ARCH; + +/* Type of the operand: Register,Immediate,Memory access,flag or bit. */ + +union _maxq20_op +{ + const reg_entry * reg; + char imms; /* This is to store the immediate value operand. */ + expressionS * disps; + symbolS * data; + const mem_access * mem; + int flag; + const reg_bit * r_bit; +}; + +typedef union _maxq20_op maxq20_opcode; + +/* For handling optional L/S in Maxq20. */ +#ifdef BFD_ASSEMBLER + +/* Exposed For Linker - maps indirectly to the liker relocations. */ +#define LONG_PREFIX MAXQ_LONGJUMP /* BFD_RELOC_16 */ +#define SHORT_PREFIX MAXQ_SHORTJUMP /* BFD_RELOC_16_PCREL_S2 */ +#define ABSOLUTE_ADDR_FOR_DATA MAXQ_INTERSEGMENT + +#define NO_PREFIX 0 +#define EXPLICT_LONG_PREFIX 14 + +#else + +#define EXPLICT_LONG_PREFIX 14 +#define LONG_PREFIX 5 +#define SHORT_PREFIX 1 +#define ABSOLUTE_ADDR_FOR_DATA 0 +#define NO_PREFIX 0 + +#endif + +/* The main instruction structure containing fields to describe instrn */ +typedef struct _maxq20_insn +{ + /* The opcode information for the MAXQ20 */ + MAXQ20_OPCODE_INFO op; + + /* The number of operands */ + unsigned int operands; + + /* Number of different types of operands - Comments can be removed if reqd. + */ + unsigned int reg_operands, mem_operands, disp_operands, data_operands; + unsigned int imm_operands, imm_bit_operands, bit_operands, flag_operands; + + /* Types of the individual operands */ + UNKNOWN_OP types[MAX_OPERANDS]; + + /* Relocation type for operand : to be investigated into */ + int reloc[MAX_OPERANDS]; + + /* Complete information of the Operands */ + maxq20_opcode maxq20_op[MAX_OPERANDS]; + + /* Choice of prefix register whenever needed */ + int prefix; + + /* Optional Prefix for Instructions like LJUMP, SJUMP etc */ + unsigned char Instr_Prefix; + + /* 16 bit Instruction word */ + unsigned char instr[2]; +} +maxq20_insn; + +/* Definitions of all possible characters that can start an operand. */ +const char *extra_symbol_chars = "@(#"; + +/* Special Character that would start a comment. */ +const char comment_chars[] = ";"; + +/* Starts a comment when it appears at the start of a line. */ +const char line_comment_chars[] = ";#"; + +const char line_separator_chars[] = ""; /* originally may b by sudeep "\n". */ + +/* The following are used for option processing. */ + +/* This is added to the mach independent string passed to getopt. */ +const char *md_shortopts = "q"; + +/* Characters for exponent and floating point. */ +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = ""; + +/* This is for the machine dependent option handling. */ +#define OPTION_EB (OPTION_MD_BASE + 0) +#define OPTION_EL (OPTION_MD_BASE + 1) +#define MAXQ_10 (OPTION_MD_BASE + 2) +#define MAXQ_20 (OPTION_MD_BASE + 3) + +struct option md_longopts[] = +{ + {"MAXQ10", no_argument, NULL, MAXQ_10}, + {"MAXQ20", no_argument, NULL, MAXQ_20}, + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof (md_longopts); + +/* md_undefined_symbol We have no need for this function. */ + +symbolS * +md_undefined_symbol (char * name ATTRIBUTE_UNUSED) +{ + return NULL; +} + +int +md_parse_option (int c, char *arg ATTRIBUTE_UNUSED) +{ + /* Any options support will be added onto this switch case. */ + switch (c) + { + case MAXQ_10: + max_version = 10; + break; + case MAXQ_20: + max_version = 20; + break; + + default: + return 0; + } + + return 1; +} + +/* When a usage message is printed, this function is called and + it prints a description of the machine specific options. */ + +void +md_show_usage (FILE * stream) +{ + /* Over here we will fill the description of the machine specific options. */ + + fprintf (stream, _(" MAXQ-specific assembler options:\n")); + + fprintf (stream, _("\ + -MAXQ20 generate obj for MAXQ20(default)\n\ + -MAXQ10 generate obj for MAXQ10\n\ + ")); +} + +#ifdef BFD_ASSEMBLER +unsigned long +maxq20_mach (void) +{ + if (!(strcmp (default_arch, "MAXQ20"))) + return 0; + + as_fatal (_("Unknown architecture")); + return 1; +} + +arelent * +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) +{ + arelent *rel; + bfd_reloc_code_real_type code; + + switch (fixp->fx_r_type) + { + case MAXQ_INTERSEGMENT: + case MAXQ_LONGJUMP: + case BFD_RELOC_16_PCREL_S2: + code = fixp->fx_r_type; + break; + + case 0: + default: + switch (fixp->fx_size) + { + default: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("can not do %d byte relocation"), fixp->fx_size); + code = BFD_RELOC_32; + break; + + case 1: + code = BFD_RELOC_8; + break; + case 2: + code = BFD_RELOC_16; + break; + case 4: + code = BFD_RELOC_32; + break; + } + } + + rel = xmalloc (sizeof (arelent)); + rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + + rel->address = fixp->fx_frag->fr_address + fixp->fx_where; + rel->addend = fixp->fx_addnumber; + rel->howto = bfd_reloc_type_lookup (stdoutput, code); + + if (rel->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent relocation type %s"), + bfd_get_reloc_code_name (code)); + + /* Set howto to a garbage value so that we can keep going. */ + rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32); + assert (rel->howto != NULL); + } + + return rel; +} + +#endif + +/* md_estimate_size_before_relax() + + Called just before relax() for rs_machine_dependent frags. The MAXQ + assembler uses these frags to handle 16 bit absolute jumps which require a + prefix instruction to be inserted. Any symbol that is now undefined will + not become defined. Return the correct fr_subtype in the frag. Return the + initial "guess for variable size of frag"(This will be eiter 2 or 0) to + caller. The guess is actually the growth beyond the fixed part. Whatever + we do to grow the fixed or variable part contributes to our returned + value. */ + +int +md_estimate_size_before_relax (fragS *fragP, segT segment) +{ + /* Check whether the symbol has been resolved or not. + Otherwise we will have to generate a fixup. */ + if ((S_GET_SEGMENT (fragP->fr_symbol) != segment) + || fragP->fr_subtype == EXPLICT_LONG_PREFIX) + { + RELOC_ENUM reloc_type; + unsigned char *opcode; + int old_fr_fix; + + /* Now this symbol has not been defined in this file. + Hence we will have to create a fixup. */ + int size = 2; + + /* This is for the prefix instruction. */ + + if (fragP->fr_subtype == EXPLICT_LONG_PREFIX) + fragP->fr_subtype = LONG_PREFIX; + + if (S_GET_SEGMENT (fragP->fr_symbol) != segment + && ((!(fragP->fr_subtype) == EXPLICT_LONG_PREFIX))) + fragP->fr_subtype = ABSOLUTE_ADDR_FOR_DATA; + + reloc_type = + (fragP->fr_subtype ? fragP->fr_subtype : ABSOLUTE_ADDR_FOR_DATA); + + fragP->fr_subtype = reloc_type; + + if (reloc_type == SHORT_PREFIX) + size = 0; + old_fr_fix = fragP->fr_fix; + opcode = (unsigned char *) fragP->fr_opcode; + + fragP->fr_fix += (size); + + fix_new (fragP, old_fr_fix - 2, size + 2, + fragP->fr_symbol, fragP->fr_offset, 0, reloc_type); + frag_wane (fragP); + return fragP->fr_fix - old_fr_fix; + } + + if (fragP->fr_subtype == SHORT_PREFIX) + { + fragP->fr_subtype = SHORT_PREFIX; + return 0; + } + + if (fragP->fr_subtype == NO_PREFIX || fragP->fr_subtype == LONG_PREFIX) + { + unsigned long instr; + unsigned long call_addr; + long diff; + fragS *f; + diff = diff ^ diff;; + call_addr = call_addr ^ call_addr; + instr = 0; + f = NULL; + + /* segment_info_type *seginfo = seg_info (segment); */ + instr = fragP->fr_address + fragP->fr_fix - 2; + + /* This is the offset if it is a PC relative jump. */ + call_addr = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset; + diff = (call_addr - instr); + + if (diff >= (-128 * 2) && diff <= (2 * 127)) + { + /* Now as offset is an 8 bit value, we will pass + that to the jump instruction directly. */ + fragP->fr_subtype = NO_PREFIX; + return 0; + } + + fragP->fr_subtype = LONG_PREFIX; + return 2; + } + + as_fatal (_("Illegal Reloc type in md_estimate_size_before_relax for line : %d"), + frag_now->fr_line); + return 0; +} + +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +/* Turn a string in input_line_pointer into a floating point constant of type + TYPE, and store the appropriate bytes in *LITP. The number of LITTLENUMS + emitted is stored in *SIZEP. An error message is returned, or NULL on OK. */ + +char * +md_atof (int type, char * litP, int * sizeP) +{ + int prec; + LITTLENUM_TYPE words[4]; + char *t; + int i; + + switch (type) + { + case 'f': + prec = 2; + break; + + case 'd': + prec = 2; + /* The size of Double has been changed to 2 words ie 32 bits. */ + /* prec = 4; */ + break; + + default: + *sizeP = 0; + return _("bad call to md_atof"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * 2; + + for (i = prec - 1; i >= 0; i--) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + + return NULL; +} + +void +maxq20_cons_fix_new (fragS * frag, unsigned int off, unsigned int len, + expressionS * exp) +{ + int r = 0; + + switch (len) + { + case 2: + r = MAXQ_WORDDATA; /* Word+n */ + break; + case 4: + r = MAXQ_LONGDATA; /* Long+n */ + break; + } + + fix_new_exp (frag, off, len, exp, 0, r); + return; +} + +short +tc_coff_fix2rtype (fixS * fixP) +{ + return fixP->fx_r_type; +} + +int +tc_coff_sizemachdep (fragS *fragP) +{ + if (fragP->fr_next) + return (fragP->fr_next->fr_address - fragP->fr_address); + + return 0; +} + +/* GAS will call this for every rs_machine_dependent fragment. The + instruction is compleated using the data from the relaxation pass. It may + also create any necessary relocations. */ +#ifdef BFD_ASSEMBLER +void +md_convert_frag (bfd * headers ATTRIBUTE_UNUSED, + segT seg ATTRIBUTE_UNUSED, + fragS * fragP) +#else +void +md_convert_frag (object_headers * headers ATTRIBUTE_UNUSED, + segT sec ATTRIBUTE_UNUSED, + fragS * fragP) +#endif +{ + unsigned char *opcode; + offsetT target_address; + offsetT opcode_address; + offsetT displacement_from_opcode_start; + int address; + + opcode = fragP->fr_opcode; + address = 0; + target_address = opcode_address = displacement_from_opcode_start = 0; + + target_address = + (S_GET_VALUE (fragP->fr_symbol) / MAXQ_OCTETS_PER_BYTE) + + (fragP->fr_offset / MAXQ_OCTETS_PER_BYTE); + + opcode_address = + (fragP->fr_address / MAXQ_OCTETS_PER_BYTE) + + ((fragP->fr_fix - 2) / MAXQ_OCTETS_PER_BYTE); + + displacement_from_opcode_start = (target_address - opcode_address); + + if ((displacement_from_opcode_start >= -128 + && displacement_from_opcode_start <= 127) + && (fragP->fr_subtype == SHORT_PREFIX + || fragP->fr_subtype == NO_PREFIX)) + { + /* Its a displacement. */ + char *p = (char *) &opcode[0]; + + *p = (char) displacement_from_opcode_start; + } + else + { + /* Its an absolute 16 bit jump. Now we have to + load the prefix operator with the upper 8 bits. */ + if (fragP->fr_subtype == SHORT_PREFIX) + { + as_bad (_("Cant make long jump/call into short jump/call : %d"), + fragP->fr_line); + return; + } + + /* Check whether the symbol has been resolved or not. + Otherwise we will have to generate a fixup. */ + + if (fragP->fr_subtype != SHORT_PREFIX) + { + RELOC_ENUM reloc_type; + unsigned char *opcode; + int old_fr_fix; + int size = 2; + + /* Now this is a basolute jump/call. + Hence we will have to create a fixup. */ + if (fragP->fr_subtype == NO_PREFIX) + fragP->fr_subtype = LONG_PREFIX; + + reloc_type = + (fragP->fr_subtype ? fragP->fr_subtype : LONG_PREFIX); + + if (reloc_type == 1) + size = 0; + old_fr_fix = fragP->fr_fix; + opcode = (unsigned char *) fragP->fr_opcode; + + fragP->fr_fix += (size); + + fix_new (fragP, old_fr_fix - 2, size + 2, + fragP->fr_symbol, fragP->fr_offset, 0, reloc_type); + frag_wane (fragP); + } + } +} + +long +md_pcrel_from (fixS *fixP) +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* Writes the val to the buf, where n is the nuumber of bytes to write. */ + +void +maxq_number_to_chars (char *buf, valueT val, int n) +{ + if (target_big_endian) + number_to_chars_bigendian (buf, val, n); + else + number_to_chars_littleendian (buf, val, n); +} + +/* GAS will call this for each fixup. It's main objective is to store the + correct value in the object file. 'fixup_segment' performs the generic + overflow check on the 'valueT *val' argument after md_apply_fix3 returns. + If the overflow check is relevant for the target machine, then + 'md_apply_fix3' should modify 'valueT *val', typically to the value stored + in the object file (not to be done in MAXQ). */ + +void +md_apply_fix3 (fixS *fixP, valueT *valT, segT seg ATTRIBUTE_UNUSED) +{ + char *p = fixP->fx_frag->fr_literal + fixP->fx_where; + char *frag_to_fix_at = + fixP->fx_frag->fr_literal + fixP->fx_frag->fr_fix - 2; + + if (fixP) + { + if (fixP->fx_frag && valT) + { + /* If the relaxation substate is not defined we make it equal + to the kind of relocation the fixup is generated for. */ + if (!fixP->fx_frag->fr_subtype) + fixP->fx_frag->fr_subtype = fixP->fx_r_type; + + /* For any instruction in which either we have specified an + absolute address or it is a long jump we need to add a PFX0 + instruction to it. In this case as the instruction has already + being written at 'fx_where' in the frag we copy it at the end of + the frag(which is where the relocation was generated) as when + the relocation is generated the frag is grown by 2 type, this is + where we copy the contents of fx_where and add a pfx0 at + fx_where. */ + if ((fixP->fx_frag->fr_subtype == ABSOLUTE_ADDR_FOR_DATA) + || (fixP->fx_frag->fr_subtype == LONG_PREFIX)) + { + *(frag_to_fix_at + 1) = *(p + 1); + maxq_number_to_chars (p + 1, PFX0, 1); + } + +#ifdef BFD_ASSEMBLER + /* Remember value for tc_gen_reloc. */ + fixP->fx_addnumber = *valT; +#endif + } + + /* This prob can be fixed by defining tc_fix_adjustable. */ +#ifndef BFD_ASSEMBLER + if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy)) + segment_info[S_GET_SEGMENT (fixP->fx_addsy)].dot = NULL; +#endif + + /* Some fixups generated by GAS which gets resovled before this this + func. is called need to be wriiten to the frag as here we are going + to go away with the relocations fx_done=1. */ + if (fixP->fx_addsy == NULL) + { + maxq_number_to_chars (p, *valT, fixP->fx_size); + fixP->fx_addnumber = *valT; + fixP->fx_done = 1; + } + } +} + +/* Tables for lexical analysis. */ +static char mnemonic_chars[256]; +static char register_chars[256]; +static char operand_chars[256]; +static char identifier_chars[256]; +static char digit_chars[256]; + +/* Lexical Macros. */ +#define is_mnemonic_char(x) (mnemonic_chars[(unsigned char)(x)]) +#define is_register_char(x) (register_chars[(unsigned char)(x)]) +#define is_operand_char(x) (operand_chars[(unsigned char)(x)]) +#define is_space_char(x) (x==' ') +#define is_identifier_char(x) (identifier_chars[(unsigned char)(x)]) +#define is_digit_char(x) (identifier_chars[(unsigned char)(x)]) + +/* Special characters for operands. */ +static char operand_special_chars[] = "[]@.-+"; + +/* md_assemble() will always leave the instruction passed to it unaltered. + To do this we store the instruction in a special stack. */ +static char save_stack[32]; +static char *save_stack_p; + +#define END_STRING_AND_SAVE(s) \ + do \ + { \ + *save_stack_p++ = *(s); \ + *s = '\0'; \ + } \ + while (0) + +#define RESTORE_END_STRING(s) \ + do \ + { \ + *(s) = *(--save_stack_p); \ + } \ + while (0) + +/* The instruction we are assembling. */ +static maxq20_insn i; + +/* The current template. */ +static MAXQ20_OPCODES *current_templates; + +/* The displacement operand if any. */ +static expressionS disp_expressions; + +/* Current Operand we are working on (0:1st operand,1:2nd operand). */ +static int this_operand; + +/* The prefix instruction if used. */ +static char PFX_INSN[2]; +static char INSERT_BUFFER[2]; + +/* For interface with expression() ????? */ +extern char *input_line_pointer; + +/* The HASH Tables: */ + +/* Operand Hash Table. */ +static struct hash_control *op_hash; + +/* Register Hash Table. */ +static struct hash_control *reg_hash; + +/* Memory reference Hash Table. */ +static struct hash_control *mem_hash; + +/* Bit hash table. */ +static struct hash_control *bit_hash; + +/* Memory Access syntax table. */ +static struct hash_control *mem_syntax_hash; + +/* This is a mapping from pseudo-op names to functions. */ + +const pseudo_typeS md_pseudo_table[] = +{ + {"int", cons, 2}, /* size of 'int' has been changed to 1 word + (i.e) 16 bits. */ + {NULL, 0, 0}, +}; + +#if defined(BFD_HEADERS) +#ifdef RELSZ +const int md_reloc_size = RELSZ; /* Coff headers. */ +#else +const int md_reloc_size = 12; /* Something else headers. */ +#endif +#else +const int md_reloc_size = 12; /* Not bfdized. */ +#endif + +#define SET_PFX_ARG(x) (PFX_INSN[1] = x) + + +/* This function sets the PFX value coresponding to the specs. Source + Destination Index Selection ---------------------------------- Write To| + SourceRegRange | Dest Addr Range + ------------------------------------------------------ PFX[0] | 0h-Fh | + 0h-7h PFX[1] | 10h-1Fh | 0h-7h PFX[2] | 0h-Fh | 8h-Fh PFX[3] | 10h-1Fh | + 8h-Fh PFX[4] | 0h-Fh | 10h-17h PFX[5] | 10h-1Fh | 10h-17h PFX[6] | 0h-Fh | + 18h-1Fh PFX[7] | 0h-Fh | 18h-1Fh */ + +static void +set_prefix (void) +{ + short int src_index = 0, dst_index = 0; + + if (i.operands == 0) + return; + if (i.operands == 1) /* Only SRC is Present */ + { + if (i.types[0] == REG) + { + if (!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI")) + { + dst_index = i.maxq20_op[0].reg[0].Mod_index; + src_index = 0x00; + } + else + { + src_index = i.maxq20_op[0].reg[0].Mod_index; + dst_index = 0x00; + } + } + } + + if (i.operands == 2) + { + if (i.types[0] == REG && i.types[1] == REG) + { + dst_index = i.maxq20_op[0].reg[0].Mod_index; + src_index = i.maxq20_op[1].reg[0].Mod_index; + } + else if (i.types[0] != REG && i.types[1] == REG) /* DST is Absent */ + { + src_index = i.maxq20_op[1].reg[0].Mod_index; + dst_index = 0x00; + } + else if (i.types[0] == REG && i.types[1] != REG) /* Id SRC is Absent */ + { + dst_index = i.maxq20_op[0].reg[0].Mod_index; + src_index = 0x00; + } + else if (i.types[0] == BIT && i.maxq20_op[0].r_bit) + { + dst_index = i.maxq20_op[0].r_bit->reg->Mod_index; + src_index = 0x00; + } + + else if (i.types[1] == BIT && i.maxq20_op[1].r_bit) + { + dst_index = 0x00; + src_index = i.maxq20_op[1].r_bit->reg->Mod_index; + } + } + + if (src_index >= 0x00 && src_index <= 0xF) + { + if (dst_index >= 0x00 && dst_index <= 0x07) + /* Set PFX[0] */ + i.prefix = 0; + + else if (dst_index >= 0x08 && dst_index <= 0x0F) + /* Set PFX[2] */ + i.prefix = 2; + + else if (dst_index >= 0x10 && dst_index <= 0x17) + /* Set PFX[4] */ + i.prefix = 4; + + else if (dst_index >= 0x18 && dst_index <= 0x1F) + /* Set PFX[6] */ + i.prefix = 6; + } + else if (src_index >= 0x10 && src_index <= 0x1F) + { + if (dst_index >= 0x00 && dst_index <= 0x07) + /* Set PFX[1] */ + i.prefix = 1; + + else if (dst_index >= 0x08 && dst_index <= 0x0F) + /* Set PFX[3] */ + i.prefix = 3; + + else if (dst_index >= 0x10 && dst_index <= 0x17) + /* Set PFX[5] */ + i.prefix = 5; + + else if (dst_index >= 0x18 && dst_index <= 0x1F) + /* Set PFX[7] */ + i.prefix = 7; + } +} + +static unsigned char +is_a_LSinstr (const char *ln_pointer) +{ + int i = 0; + + for (i = 0; LSInstr[i] != NULL; i++) + if (!strcmp (LSInstr[i], ln_pointer)) + return 1; + + return 0; +} + +static void +LS_processing (const char *line) +{ + if (is_a_LSinstr (line)) + { + if ((line[0] == 'L') || (line[0] == 'l')) + { + i.prefix = 0; + INSERT_BUFFER[0] = PFX0; + i.Instr_Prefix = LONG_PREFIX; + } + else if ((line[0] == 'S') || (line[0] == 's')) + i.Instr_Prefix = SHORT_PREFIX; + else + i.Instr_Prefix = NO_PREFIX; + } + else + i.Instr_Prefix = LONG_PREFIX; +} + +/* Separate mnemonics and the operands. */ + +static char * +parse_insn (char *line, char *mnemonic) +{ + char *l = line; + char *token_start = l; + char *mnem_p; + char temp[MAX_MNEM_SIZE]; + int ii = 0; + + memset (temp, END_OF_INSN, MAX_MNEM_SIZE); + mnem_p = mnemonic; + + while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0) + { + ii++; + mnem_p++; + if (mnem_p >= mnemonic + MAX_MNEM_SIZE) + { + as_bad (_("no such instruction: `%s'"), token_start); + return NULL; + } + l++; + } + + if (!is_space_char (*l) && *l != END_OF_INSN) + { + as_bad (_("invalid character %s in mnemonic"), l); + return NULL; + } + + while (ii) + { + temp[ii - 1] = toupper ((char) mnemonic[ii - 1]); + ii--; + } + + LS_processing (temp); + + if (i.Instr_Prefix != 0 && is_a_LSinstr (temp)) + /* Skip the optional L-S. */ + memcpy (temp, temp + 1, MAX_MNEM_SIZE); + + /* Look up instruction (or prefix) via hash table. */ + current_templates = (MAXQ20_OPCODES *) hash_find (op_hash, temp); + + if (current_templates != NULL) + return l; + + as_bad (_("no such instruction: `%s'"), token_start); + return NULL; +} + +/* Function to calculate x to the power of y. + Just to avoid including the math libraries. */ + +static int +pwr (int x, int y) +{ + int k, ans = 1; + + for (k = 0; k < y; k++) + ans *= x; + + return ans; +} + +static reg_entry * +parse_reg_by_index (char *imm_start) +{ + int k = 0, mid = 0, rid = 0, val = 0, j = 0; + char temp[4] = { 0 }; + reg_entry *reg = NULL; + + do + { + if (isdigit (imm_start[k])) + temp[k] = imm_start[k] - '0'; + + else if (isalpha (imm_start[k]) + && (imm_start[k] = tolower (imm_start[k])) < 'g') + temp[k] = 10 + (int) (imm_start[k] - 'a'); + + else if (imm_start[k] == 'h') + break; + + else if (imm_start[k] == END_OF_INSN) + { + imm_start[k] = 'd'; + break; + } + + else + return NULL; /* not a hex digit */ + + k++; + } + while (imm_start[k] != '\n'); + + switch (imm_start[k]) + { + case 'h': + for (j = 0; j < k; j++) + val += temp[j] * pwr (16, k - j - 1); + break; + + case 'd': + for (j = 0; j < k; j++) + { + if (temp[j] > 9) + return NULL; /* not a number */ + + val += temp[j] * pwr (10, k - j - 1); + break; + } + } + + /* Get the module and register id's. */ + mid = val & 0x0f; + rid = (val >> 4) & 0x0f; + + if (mid < 6) + { + /* Search the pheripheral reg table. */ + for (j = 0; j < num_of_reg; j++) + { + if (new_reg_table[j].opcode == val) + { + reg = (reg_entry *) & new_reg_table[j]; + break; + } + } + } + + else + { + /* Search the system register table. */ + j = 0; + + while (system_reg_table[j].reg_name != NULL) + { + if (system_reg_table[j].opcode == val) + { + reg = (reg_entry *) & system_reg_table[j]; + break; + } + j++; + } + } + + if (reg == NULL) + { + as_bad (_("Invalid register value %s"), imm_start); + return reg; + } + +#if CHANGE_PFX + if (this_operand == 0 && reg != NULL) + { + if (reg->Mod_index > 7) + i.prefix = 2; + else + i.prefix = 0; + } +#endif + return (reg_entry *) reg; +} + +/* REG_STRING starts *before* REGISTER_PREFIX. */ + +static reg_entry * +parse_register (char *reg_string, char **end_op) +{ + char *s = reg_string; + char *p = NULL; + char reg_name_given[MAX_REG_NAME_SIZE + 1]; + reg_entry *r = NULL; + + r = NULL; + p = NULL; + + /* Skip possible REGISTER_PREFIX and possible whitespace. */ + if (is_space_char (*s)) + ++s; + + p = reg_name_given; + while ((*p++ = register_chars[(unsigned char) *s]) != '\0') + { + if (p >= reg_name_given + MAX_REG_NAME_SIZE) + return (reg_entry *) NULL; + s++; + } + + *end_op = s; + + r = (reg_entry *) hash_find (reg_hash, reg_name_given); + +#if CHANGE_PFX + if (this_operand == 0 && r != NULL) + { + if (r->Mod_index > 7) + i.prefix = 2; + else + i.prefix = 0; + } +#endif + return r; +} + +static reg_bit * +parse_register_bit (char *reg_string, char **end_op) +{ + const char *s = reg_string; + short k = 0; + char diff = 0; + reg_bit *rb = NULL; + reg_entry *r = NULL; + bit_name *b = NULL; + char temp_bitname[MAX_REG_NAME_SIZE + 2]; + char temp[MAX_REG_NAME_SIZE + 1]; + + memset (&temp, '\0', (MAX_REG_NAME_SIZE + 1)); + memset (&temp_bitname, '\0', (MAX_REG_NAME_SIZE + 2)); + + diff = 0; + r = NULL; + rb = NULL; + rb = xmalloc (sizeof (reg_bit)); + rb->reg = xmalloc (sizeof (reg_entry)); + k = 0; + + /* For supporting bit names. */ + b = (bit_name *) hash_find (bit_hash, reg_string); + + if (b != NULL) + { + *end_op = reg_string + strlen (reg_string); + strcpy (temp_bitname, b->reg_bit); + s = temp_bitname; + } + + if (strchr (s, '.')) + { + while (*s != '.') + { + if (*s == '\0') + return NULL; + temp[k] = *s++; + + k++; + } + temp[k] = '\0'; + } + + if ((r = parse_register (temp, end_op)) == NULL) + return NULL; + + rb->reg = r; + + /* Skip the "." */ + s++; + + if (isdigit ((char) *s)) + rb->bit = atoi (s); + else if (isalpha ((char) *s)) + { + rb->bit = (char) *s - 'a'; + rb->bit += 10; + if (rb->bit > 15) + { + as_bad (_("Invalid bit number : '%c'"), (char) *s); + return NULL; + } + } + + if (b != NULL) + diff = strlen (temp_bitname) - strlen (temp) - 1; + else + diff = strlen (reg_string) - strlen (temp) - 1; + + if (*(s + diff) != '\0') + { + as_bad (_("Illegal character after operand '%s'"), reg_string); + return NULL; + } + + return rb; +} + +static void +pfx_for_imm_val (int arg) +{ + if (i.prefix == -1) + return; + + if (i.prefix == 0 && arg == 0 && PFX_INSN[1] == 0 && !(i.data_operands)) + return; + + if (!(i.prefix < 0) && !(i.prefix > 7)) + PFX_INSN[0] = (i.prefix << 4) | PFX0; + + if (!PFX_INSN[1]) + PFX_INSN[1] = arg; + +} + +static int +maxq20_immediate (char *imm_start) +{ + int val = 0, val_pfx = 0; + char sign_val = 0; + int k = 0, j; + int temp[4] = { 0 }; + + imm_start++; + + if (imm_start[1] == '\0' && (imm_start[0] == '0' || imm_start[0] == '1') + && (this_operand == 1 && ((i.types[0] == BIT || i.types[0] == FLAG)))) + { + val = imm_start[0] - '0'; + i.imm_bit_operands++; + i.types[this_operand] = IMMBIT; + i.maxq20_op[this_operand].imms = (char) val; +#if CHANGE_PFX + if (i.prefix == 2) + pfx_for_imm_val (0); +#endif + return 1; + } + + /* Check For Sign Charcater. */ + sign_val = 0; + + do + { + if (imm_start[k] == '-' && k == 0) + sign_val = -1; + + else if (imm_start[k] == '+' && k == 0) + sign_val = 1; + + else if (isdigit (imm_start[k])) + temp[k] = imm_start[k] - '0'; + + else if (isalpha (imm_start[k]) + && (imm_start[k] = tolower (imm_start[k])) < 'g') + temp[k] = 10 + (int) (imm_start[k] - 'a'); + + else if (imm_start[k] == 'h') + break; + + else if (imm_start[k] == '\0') + { + imm_start[k] = 'd'; + break; + } + else + { + as_bad (_("Invalid Character in immediate Value : %c"), + imm_start[k]); + return 0; + } + k++; + } + while (imm_start[k] != '\n'); + + switch (imm_start[k]) + { + case 'h': + for (j = (sign_val ? 1 : 0); j < k; j++) + val += temp[j] * pwr (16, k - j - 1); + break; + + case 'd': + for (j = (sign_val ? 1 : 0); j < k; j++) + { + if (temp[j] > 9) + { + as_bad (_("Invalid Character in immediate value : %c"), + imm_start[j]); + return 0; + } + val += temp[j] * pwr (10, k - j - 1); + } + } + + if (!sign_val) + sign_val = 1; + + /* Now over here the value val stores the 8 bit/16 bit value. We will put a + check if we are moving a 16 bit immediate value into an 8 bit register. + In that case we will generate a warning and move only the lower 8 bits */ + if (val > 65535) + { + as_bad (_("Immediate value greater than 16 bits")); + return 0; + } + + val = val * sign_val; + + /* If it is a stack pointer and the value is greater than the maximum + permissible size */ + if (this_operand == 1) + { + if ((val * sign_val) > MAX_STACK && i.types[0] == REG + && !strcmp (i.maxq20_op[0].reg->reg_name, "SP")) + { + as_warn (_ + ("Attempt to move a value in the stack pointer greater than the size of the stack")); + val = val & MAX_STACK; + } + + /* Check the range for 8 bit registers. */ + else if (((val * sign_val) > 0xFF) && (i.types[0] == REG) + && (i.maxq20_op[0].reg->rtype == Reg_8W)) + { + as_warn (_ + ("Attempt to move 16 bit value into an 8 bit register.Truncating..\n")); + val = val & 0xfe; + } + + else if (((sign_val == -1) || (val > 0xFF)) && (i.types[0] == REG) + && (i.maxq20_op[0].reg->rtype == Reg_8W)) + { + val_pfx = val >> 8; + val = ((val) & 0x00ff); + SET_PFX_ARG (val_pfx); + i.maxq20_op[this_operand].imms = (char) val; + } + + else if ((val <= 0xff) && (i.types[0] == REG) + && (i.maxq20_op[0].reg->rtype == Reg_8W)) + i.maxq20_op[this_operand].imms = (char) val; + + + /* Check for 16 bit registers. */ + else if (((sign_val == -1) || val > 0xFE) && i.types[0] == REG + && i.maxq20_op[0].reg->rtype == Reg_16W) + { + /* Add PFX for any negative value -> 16bit register. */ + val_pfx = val >> 8; + val = ((val) & 0x00ff); + SET_PFX_ARG (val_pfx); + i.maxq20_op[this_operand].imms = (char) val; + } + + else if (val < 0xFF && i.types[0] == REG + && i.maxq20_op[0].reg->rtype == Reg_16W) + { + i.maxq20_op[this_operand].imms = (char) val; + } + + /* All the immediate memory access - no PFX. */ + else if (i.types[0] == MEM) + { + if ((sign_val == -1) || val > 0xFE) + { + val_pfx = val >> 8; + val = ((val) & 0x00ff); + SET_PFX_ARG (val_pfx); + i.maxq20_op[this_operand].imms = (char) val; + } + else + i.maxq20_op[this_operand].imms = (char) val; + } + + /* Special handling for immediate jumps like jump nz, #03h etc. */ + else if (val < 0xFF && i.types[0] == FLAG) + i.maxq20_op[this_operand].imms = (char) val; + + else if ((((sign_val == -1) || val > 0xFE)) && i.types[0] == FLAG) + { + val_pfx = val >> 8; + val = ((val) & 0x00ff); + SET_PFX_ARG (val_pfx); + i.maxq20_op[this_operand].imms = (char) val; + } + else + { + as_bad (_("Invalid immediate move operation")); + return 0; + } + } + else + { + /* All the instruction with operation on ACC: like ADD src, etc. */ + if ((sign_val == -1) || val > 0xFE) + { + val_pfx = val >> 8; + val = ((val) & 0x00ff); + SET_PFX_ARG (val_pfx); + i.maxq20_op[this_operand].imms = (char) val; + } + else + i.maxq20_op[this_operand].imms = (char) val; + } + + i.imm_operands++; + return 1; +} + +static int +extract_int_val (const char *imm_start) +{ + int k, j, val; + char sign_val; + int temp[4]; + + k = 0; + j = 0; + val = 0; + sign_val = 0; + do + { + if (imm_start[k] == '-' && k == 0) + sign_val = -1; + + else if (imm_start[k] == '+' && k == 0) + sign_val = 1; + + else if (isdigit (imm_start[k])) + temp[k] = imm_start[k] - '0'; + + else if (isalpha (imm_start[k]) && (tolower (imm_start[k])) < 'g') + temp[k] = 10 + (int) (tolower (imm_start[k]) - 'a'); + + else if (tolower (imm_start[k]) == 'h') + break; + + else if ((imm_start[k] == '\0') || (imm_start[k] == ']')) + /* imm_start[k]='d'; */ + break; + + else + { + as_bad (_("Invalid Character in immediate Value : %c"), + imm_start[k]); + return 0; + } + k++; + } + while (imm_start[k] != '\n'); + + switch (imm_start[k]) + { + case 'h': + for (j = (sign_val ? 1 : 0); j < k; j++) + val += temp[j] * pwr (16, k - j - 1); + break; + + default: + for (j = (sign_val ? 1 : 0); j < k; j++) + { + if (temp[j] > 9) + { + as_bad (_("Invalid Character in immediate value : %c"), + imm_start[j]); + return 0; + } + val += temp[j] * pwr (10, k - j - 1); + } + } + + if (!sign_val) + sign_val = 1; + + return val * sign_val; +} + +static char +check_for_parse (const char *line) +{ + int val; + + if (*(line + 1) == '[') + { + do + { + line++; + if ((*line == '-') || (*line == '+')) + break; + } + while (!is_space_char (*line)); + + if ((*line == '-') || (*line == '+')) + val = extract_int_val (line); + else + val = extract_int_val (line + 1); + + INSERT_BUFFER[0] = 0x3E; + INSERT_BUFFER[1] = val; + + return 1; + } + + return 0; +} + +static mem_access * +maxq20_mem_access (char *mem_string, char **end_op) +{ + char *s = mem_string; + char *p; + char mem_name_given[MAX_MEM_NAME_SIZE + 1]; + mem_access *m; + + m = NULL; + + /* Skip possible whitespace. */ + if (is_space_char (*s)) + ++s; + + p = mem_name_given; + while ((*p++ = register_chars[(unsigned char) *s]) != '\0') + { + if (p >= mem_name_given + MAX_MEM_NAME_SIZE) + return (mem_access *) NULL; + s++; + } + + *end_op = s; + + m = (mem_access *) hash_find (mem_hash, mem_name_given); + + return m; +} + +/* This function checks whether the operand is a variable in the data segment + and if so, it returns its symbol entry from the symbol table. */ + +static symbolS * +maxq20_data (char *op_string) +{ + symbolS *symbolP; + symbolP = symbol_find (op_string); + + if (symbolP != NULL + && S_GET_SEGMENT (symbolP) != now_seg + && S_GET_SEGMENT (symbolP) != +#ifdef BFD_ASSEMBLER + bfd_und_section_ptr +#else + SEG_UNKNOWN +#endif + ) + { + int val_pfx; + +#ifdef BFD_ASSEMBLER + val_pfx = 0; +#else + val_pfx = (symbolP->sy_value.X_add_number) >> 8; +#endif + + /* In case we do not want to always include the prefix instruction and + let the loader handle the job or in case of a 8 bit addressing mode, + we will just check for val_pfx to be equal to zero and then load the + prefix instruction. Otherwise no prefix instruction needs to be + loaded. */ + /* The prefix register will have to be loaded automatically as we have + a 16 bit addressing field. */ + pfx_for_imm_val (val_pfx); + return symbolP; + } + + return NULL; +} + +static int +maxq20_displacement (char *disp_start, char *disp_end) +{ + expressionS *exp; + segT exp_seg = 0; + char *save_input_line_pointer; +#ifndef LEX_AT + char *gotfree_input_line; +#endif + + gotfree_input_line = NULL; + exp = &disp_expressions; + i.maxq20_op[this_operand].disps = exp; + i.disp_operands++; + save_input_line_pointer = input_line_pointer; + input_line_pointer = disp_start; + + END_STRING_AND_SAVE (disp_end); + +#ifndef LEX_AT + /* gotfree_input_line = lex_got (&i.reloc[this_operand], NULL); if + (gotfree_input_line) input_line_pointer = gotfree_input_line; */ +#endif + exp_seg = expression (exp); + + SKIP_WHITESPACE (); + if (*input_line_pointer) + as_bad (_("junk `%s' after expression"), input_line_pointer); +#if GCC_ASM_O_HACK + RESTORE_END_STRING (disp_end + 1); +#endif + RESTORE_END_STRING (disp_end); + input_line_pointer = save_input_line_pointer; +#ifndef LEX_AT + if (gotfree_input_line) + free (gotfree_input_line); +#endif + if (exp->X_op == O_absent || exp->X_op == O_big) + { + /* Missing or bad expr becomes absolute 0. */ + as_bad (_("missing or invalid displacement expression `%s' taken as 0"), + disp_start); + exp->X_op = O_constant; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_op_symbol = (symbolS *) 0; + } +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + + if (exp->X_op != O_constant +#ifdef BFD_ASSEMBLER + && OUTPUT_FLAVOR == bfd_target_aout_flavour +#endif + && exp_seg != absolute_section + && exp_seg != text_section + && exp_seg != data_section + && exp_seg != bss_section && exp_seg != undefined_section +#ifdef BFD_ASSEMBLER + && !bfd_is_com_section (exp_seg) +#endif + ) + { +#ifdef BFD_ASSEMBLER + as_bad (_("unimplemented segment %s in operand"), exp_seg->name); +#else + as_bad (_("unimplemented segment type %d in operand"), exp_seg); +#endif + return 0; + } +#endif + i.maxq20_op[this_operand].disps = exp; + return 1; +} + +/* Parse OPERAND_STRING into the maxq20_insn structure I. + Returns non-zero on error. */ + +static int +maxq20_operand (char *operand_string) +{ + reg_entry *r = NULL; + reg_bit *rb = NULL; + mem_access *m = NULL; + char *end_op = NULL; + symbolS *sym = NULL; + char *base_string = NULL; + int ii = 0; + /* Start and end of displacement string expression (if found). */ + char *displacement_string_start = NULL; + char *displacement_string_end = NULL; + /* This maintains the case sentivness. */ + char case_str_op_string[MAX_OPERAND_SIZE + 1]; + char str_op_string[MAX_OPERAND_SIZE + 1]; + char *org_case_op_string = case_str_op_string; + char *op_string = str_op_string; + + + memset (op_string, END_OF_INSN, (MAX_OPERAND_SIZE + 1)); + memset (org_case_op_string, END_OF_INSN, (MAX_OPERAND_SIZE + 1)); + + memcpy (op_string, operand_string, strlen (operand_string) + 1); + memcpy (org_case_op_string, operand_string, strlen (operand_string) + 1); + + ii = strlen (operand_string) + 1; + + if (ii > MAX_OPERAND_SIZE) + { + as_bad (_("Size of Operand '%s' greater than %d"), op_string, + MAX_OPERAND_SIZE); + return 0; + } + + while (ii) + { + op_string[ii - 1] = toupper ((char) op_string[ii - 1]); + ii--; + } + + if (is_space_char (*op_string)) + ++op_string; + + if (isxdigit (operand_string[0])) + { + /* Now the operands can start with an Integer. */ + r = parse_reg_by_index (op_string); + if (r != NULL) + { + if (is_space_char (*op_string)) + ++op_string; + i.types[this_operand] = REG; /* Set the type. */ + i.maxq20_op[this_operand].reg = r; /* Set the Register value. */ + i.reg_operands++; + return 1; + } + + /* Get the origanal string. */ + memcpy (op_string, operand_string, strlen (operand_string) + 1); + ii = strlen (operand_string) + 1; + + while (ii) + { + op_string[ii - 1] = toupper ((char) op_string[ii - 1]); + ii--; + } + } + + /* Check for flags. */ + if (!strcmp (op_string, "Z")) + { + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = FLAG; /* Set the type. */ + i.maxq20_op[this_operand].flag = FLAG_Z; /* Set the Register value. */ + + i.flag_operands++; + + return 1; + } + + else if (!strcmp (op_string, "NZ")) + { + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = FLAG; /* Set the type. */ + i.maxq20_op[this_operand].flag = FLAG_NZ; /* Set the Register value. */ + i.flag_operands++; + return 1; + } + + else if (!strcmp (op_string, "NC")) + { + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = FLAG; /* Set the type. */ + i.maxq20_op[this_operand].flag = FLAG_NC; /* Set the Register value. */ + i.flag_operands++; + return 1; + } + + else if (!strcmp (op_string, "E")) + { + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = FLAG; /* Set the type. */ + i.maxq20_op[this_operand].flag = FLAG_E; /* Set the Register value. */ + + i.flag_operands++; + + return 1; + } + + else if (!strcmp (op_string, "S")) + { + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = FLAG; /* Set the type. */ + i.maxq20_op[this_operand].flag = FLAG_S; /* Set the Register value. */ + + i.flag_operands++; + + return 1; + } + + else if (!strcmp (op_string, "C")) + { + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = FLAG; /* Set the type. */ + i.maxq20_op[this_operand].flag = FLAG_C; /* Set the Register value. */ + + i.flag_operands++; + + return 1; + } + + else if (!strcmp (op_string, "NE")) + { + + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = FLAG; /* Set the type. */ + + i.maxq20_op[this_operand].flag = FLAG_NE; /* Set the Register value. */ + + i.flag_operands++; + + return 1; + } + + /* CHECK FOR REGISTER BIT */ + else if ((rb = parse_register_bit (op_string, &end_op)) != NULL) + { + op_string = end_op; + + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = BIT; + + i.maxq20_op[this_operand].r_bit = rb; + + i.bit_operands++; + + return 1; + } + + else if (*op_string == IMMEDIATE_PREFIX) /* FOR IMMEDITE. */ + { + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = IMM; + + if (!maxq20_immediate (op_string)) + { + as_bad (_("illegal immediate operand '%s'"), op_string); + return 0; + } + return 1; + } + + else if (*op_string == ABSOLUTE_PREFIX || !strcmp (op_string, "NUL")) + { + if (is_space_char (*op_string)) + ++op_string; + + /* For new requiremnt of copiler of for, @(BP,cons). */ + if (check_for_parse (op_string)) + { + memset (op_string, '\0', strlen (op_string) + 1); + memcpy (op_string, "@BP[OFFS]\0", 11); + } + + i.types[this_operand] = MEM; + + if ((m = maxq20_mem_access (op_string, &end_op)) == NULL) + { + as_bad (_("Invalid operand for memory access '%s'"), op_string); + return 0; + } + i.maxq20_op[this_operand].mem = m; + + i.mem_operands++; + + return 1; + } + + else if ((r = parse_register (op_string, &end_op)) != NULL) /* Check for register. */ + { + op_string = end_op; + + if (is_space_char (*op_string)) + ++op_string; + + i.types[this_operand] = REG; /* Set the type. */ + i.maxq20_op[this_operand].reg = r; /* Set the Register value. */ + i.reg_operands++; + return 1; + } + + if (this_operand == 1) + { + /* Changed for orginal case of data refrence on 30 Nov 2003. */ + /* The operand can either be a data reference or a symbol reference. */ + if ((sym = maxq20_data (org_case_op_string)) != NULL) /* Check for data memory. */ + { + while (is_space_char (*op_string)) + ++op_string; + + /* Set the type of the operand. */ + i.types[this_operand] = DATA; + + /* Set the value of the data. */ + i.maxq20_op[this_operand].data = sym; + i.data_operands++; + + return 1; + } + + else if (is_digit_char (*op_string) || is_identifier_char (*op_string)) + { + /* This is a memory reference of some sort. char *base_string; + Start and end of displacement string expression (if found). char + *displacement_string_start; char *displacement_string_end. */ + base_string = org_case_op_string + strlen (org_case_op_string); + + --base_string; + if (is_space_char (*base_string)) + --base_string; + + /* If we only have a displacement, set-up for it to be parsed + later. */ + displacement_string_start = org_case_op_string; + displacement_string_end = base_string + 1; + if (displacement_string_start != displacement_string_end) + { + if (!maxq20_displacement (displacement_string_start, + displacement_string_end)) + { + as_bad (_("illegal displacement operand ")); + return 0; + } + /* A displacement operand found. */ + i.types[this_operand] = DISP; /* Set the type. */ + return 1; + } + } + } + + /* Check for displacement. */ + else if (is_digit_char (*op_string) || is_identifier_char (*op_string)) + { + /* This is a memory reference of some sort. char *base_string; + Start and end of displacement string expression (if found). char + *displacement_string_start; char *displacement_string_end; */ + base_string = org_case_op_string + strlen (org_case_op_string); + + --base_string; + if (is_space_char (*base_string)) + --base_string; + + /* If we only have a displacement, set-up for it to be parsed later. */ + displacement_string_start = org_case_op_string; + displacement_string_end = base_string + 1; + if (displacement_string_start != displacement_string_end) + { + if (!maxq20_displacement (displacement_string_start, + displacement_string_end)) + return 0; + /* A displacement operand found. */ + i.types[this_operand] = DISP; /* Set the type. */ + } + } + return 1; +} + +/* Parse_operand takes as input instruction and operands and Parse operands + and makes entry in the template. */ + +static char * +parse_operands (char *l, const char *mnemonic) +{ + char *token_start; + + /* 1 if operand is pending after ','. */ + short int expecting_operand = 0; + + /* Non-zero if operand parens not balanced. */ + short int paren_not_balanced; + + int operand_ok; + + /* For Overcoming Warning of unused variable. */ + if (mnemonic) + operand_ok = 0; + + while (*l != END_OF_INSN) + { + /* Skip optional white space before operand. */ + if (is_space_char (*l)) + ++l; + + if (!is_operand_char (*l) && *l != END_OF_INSN) + { + as_bad (_("invalid character %c before operand %d"), + (char) (*l), i.operands + 1); + return NULL; + } + token_start = l; + + paren_not_balanced = 0; + while (paren_not_balanced || *l != ',') + { + if (*l == END_OF_INSN) + { + if (paren_not_balanced) + { + as_bad (_("unbalanced brackets in operand %d."), + i.operands + 1); + return NULL; + } + + break; + } + else if (!is_operand_char (*l) && !is_space_char (*l)) + { + as_bad (_("invalid character %c in operand %d"), + (char) (*l), i.operands + 1); + return NULL; + } + if (*l == '[') + ++paren_not_balanced; + if (*l == ']') + --paren_not_balanced; + l++; + } + + if (l != token_start) + { + /* Yes, we've read in another operand. */ + this_operand = i.operands++; + if (i.operands > MAX_OPERANDS) + { + as_bad (_("spurious operands; (%d operands/instruction max)"), + MAX_OPERANDS); + return NULL; + } + + /* Now parse operand adding info to 'i' as we go along. */ + END_STRING_AND_SAVE (l); + + operand_ok = maxq20_operand (token_start); + + RESTORE_END_STRING (l); + + if (!operand_ok) + return NULL; + } + else + { + if (expecting_operand) + { + expecting_operand_after_comma: + as_bad (_("expecting operand after ','; got nothing")); + return NULL; + } + } + + if (*l == ',') + { + if (*(++l) == END_OF_INSN) + /* Just skip it, if it's \n complain. */ + goto expecting_operand_after_comma; + + expecting_operand = 1; + } + } + + return l; +} + +static int +match_operands (int type, MAX_ARG_TYPE flag_type, MAX_ARG_TYPE arg_type, + int op_num) +{ + switch (type) + { + case REG: + if ((arg_type & A_REG) == A_REG) + return 1; + break; + case IMM: + if ((arg_type & A_IMM) == A_IMM) + return 1; + break; + case IMMBIT: + if ((arg_type & A_BIT_0) == A_BIT_0 && (i.maxq20_op[op_num].imms == 0)) + return 1; + else if ((arg_type & A_BIT_1) == A_BIT_1 + && (i.maxq20_op[op_num].imms == 1)) + return 1; + break; + case MEM: + if ((arg_type & A_MEM) == A_MEM) + return 1; + break; + + case FLAG: + if ((arg_type & flag_type) == flag_type) + return 1; + + break; + + case BIT: + if ((arg_type & ACC_BIT) == ACC_BIT && !strcmp (i.maxq20_op[op_num].r_bit->reg->reg_name, "ACC")) + return 1; + else if ((arg_type & SRC_BIT) == SRC_BIT && (op_num == 1)) + return 1; + else if ((op_num == 0) && (arg_type & DST_BIT) == DST_BIT) + return 1; + break; + case DISP: + if ((arg_type & A_DISP) == A_DISP) + return 1; + case DATA: + if ((arg_type & A_DATA) == A_DATA) + return 1; + case BIT_BUCKET: + if ((arg_type & A_BIT_BUCKET) == A_BIT_BUCKET) + return 1; + } + return 0; +} + +static int +match_template (void) +{ + /* Points to template once we've found it. */ + const MAXQ20_OPCODE_INFO *t; + char inv_oper; + inv_oper = 0; + + for (t = current_templates->start; t < current_templates->end; t++) + { + /* Must have right number of operands. */ + if (i.operands != t->op_number) + continue; + else if (!t->op_number) + break; + + switch (i.operands) + { + case 2: + if (!match_operands (i.types[1], i.maxq20_op[1].flag, t->arg[1], 1)) + { + inv_oper = 1; + continue; + } + case 1: + if (!match_operands (i.types[0], i.maxq20_op[0].flag, t->arg[0], 0)) + { + inv_oper = 2; + continue; + } + } + break; + } + + if (t == current_templates->end) + { + /* We found no match. */ + as_bad (_("operand %d is invalid for `%s'"), + inv_oper, current_templates->start->name); + return 0; + } + + /* Copy the template we have found. */ + i.op = *t; + return 1; +} + +/* This function filters out the various combinations of operands which are + not allowed for a particular instruction. */ + +static int +match_filters (void) +{ + /* Now we have at our disposal the instruction i. We will be using the + following fields i.op.name : This is the mnemonic name. i.types[2] : + These are the types of the operands (REG/IMM/DISP/MEM/BIT/FLAG/IMMBIT) + i.maxq20_op[2] : This contains the specific info of the operands. */ + + /* Our first filter : NO ALU OPERATIONS CAN HAVE THE ACTIVE ACCUMULATOR AS + SOURCE. */ + if (!strcmp (i.op.name, "AND") || !strcmp (i.op.name, "OR") + || !strcmp (i.op.name, "XOR") || !strcmp (i.op.name, "ADD") + || !strcmp (i.op.name, "ADDC") || !strcmp (i.op.name, "SUB") + || !strcmp (i.op.name, "SUBB")) + { + if (i.types[0] == REG) + { + if (i.maxq20_op[0].reg->Mod_name == 0xa) + { + as_bad (_ + ("The Accumulator cannot be used as a source in ALU instructions\n")); + return 0; + } + } + } + + if (!strcmp (i.op.name, "MOVE") && (i.types[0] == MEM || i.types[1] == MEM) + && i.operands == 2) + { + mem_access_syntax *mem_op = NULL; + + if (i.types[0] == MEM) + { + mem_op = + (mem_access_syntax *) hash_find (mem_syntax_hash, + i.maxq20_op[0].mem->name); + if ((mem_op->type == SRC) && mem_op) + { + as_bad (_("'%s' operand cant be used as destination in %s"), + mem_op->name, i.op.name); + return 0; + } + else if ((mem_op->invalid_op != NULL) && (i.types[1] == MEM) + && mem_op) + { + int k = 0; + + for (k = 0; k < 5 || !mem_op->invalid_op[k]; k++) + { + if (mem_op->invalid_op[k] != NULL) + if (!strcmp + (mem_op->invalid_op[k], i.maxq20_op[1].mem->name)) + { + as_bad (_ + ("Invalid Instruction '%s' operand cant be used with %s"), + mem_op->name, i.maxq20_op[1].mem->name); + return 0; + } + } + } + } + + if (i.types[1] == MEM) + { + mem_op = NULL; + mem_op = + (mem_access_syntax *) hash_find (mem_syntax_hash, + i.maxq20_op[1].mem->name); + if (mem_op->type == DST && mem_op) + { + as_bad (_("'%s' operand cant be used as source in %s"), + mem_op->name, i.op.name); + return 0; + } + else if (mem_op->invalid_op != NULL && i.types[0] == MEM && mem_op) + { + int k = 0; + + for (k = 0; k < 5 || !mem_op->invalid_op[k]; k++) + { + if (mem_op->invalid_op[k] != NULL) + if (!strcmp + (mem_op->invalid_op[k], i.maxq20_op[0].mem->name)) + { + as_bad (_ + ("Invalid Instruction '%s' operand cant be used with %s"), + mem_op->name, i.maxq20_op[0].mem->name); + return 0; + } + } + } + else if (i.types[0] == REG + && !strcmp (i.maxq20_op[0].reg->reg_name, "OFFS") + && mem_op) + { + if (!strcmp (mem_op->name, "@BP[OFFS--]") + || !strcmp (mem_op->name, "@BP[OFFS++]")) + { + as_bad (_ + ("Invalid Instruction '%s' operand cant be used with %s"), + mem_op->name, i.maxq20_op[0].mem->name); + return 0; + } + } + } + } + + /* Added for SRC and DST in one operand instructioni i.e OR @--DP[1] added + on 10-March-2004. */ + if ((i.types[0] == MEM) && (i.operands == 1) + && !(!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI"))) + { + mem_access_syntax *mem_op = NULL; + + if (i.types[0] == MEM) + { + mem_op = + (mem_access_syntax *) hash_find (mem_syntax_hash, + i.maxq20_op[0].mem->name); + if (mem_op->type == DST && mem_op) + { + as_bad (_("'%s' operand cant be used as source in %s"), + mem_op->name, i.op.name); + return 0; + } + } + } + + if (i.operands == 2 && i.types[0] == IMM) + { + as_bad (_("'%s' instruction cant have first operand as Immediate vale"), + i.op.name); + return 0; + } + + /* Our second filter : SP or @SP-- cannot be used with PUSH or POP */ + if (!strcmp (i.op.name, "PUSH") || !strcmp (i.op.name, "POP") + || !strcmp (i.op.name, "POPI")) + { + if (i.types[0] == REG) + { + if (!strcmp (i.maxq20_op[0].reg->reg_name, "SP")) + { + as_bad (_("SP cannot be used with %s\n"), i.op.name); + return 0; + } + } + else if (i.types[0] == MEM + && !strcmp (i.maxq20_op[0].mem->name, "@SP--")) + { + as_bad (_("@SP-- cannot be used with PUSH\n")); + return 0; + } + } + + /* This filter checks that two memory references using DP's cannot be used + together in an instruction */ + if (!strcmp (i.op.name, "MOVE") && i.mem_operands == 2) + { + if (strlen (i.maxq20_op[0].mem->name) != 6 || + strcmp (i.maxq20_op[0].mem->name, i.maxq20_op[1].mem->name)) + { + if (!strncmp (i.maxq20_op[0].mem->name, "@DP", 3) + && !strncmp (i.maxq20_op[1].mem->name, "@DP", 3)) + { + as_bad (_ + ("Operands either contradictory or use the data bus in read/write state together")); + return 0; + } + + if (!strncmp (i.maxq20_op[0].mem->name, "@SP", 3) + && !strncmp (i.maxq20_op[1].mem->name, "@SP", 3)) + { + as_bad (_ + ("Operands either contradictory or use the data bus in read/write state together")); + return 0; + } + } + if ((i.maxq20_op[1].mem != NULL) + && !strncmp (i.maxq20_op[1].mem->name, "NUL", 3)) + { + as_bad (_("MOVE Cant Use NUL as SRC")); + return 0; + } + } + + /* This filter checks that contradictory movement between DP register and + Memory access using DP followed by increment or decrement. */ + + if (!strcmp (i.op.name, "MOVE") && i.mem_operands == 1 + && i.reg_operands == 1) + { + int memnum, regnum; + + memnum = (i.types[0] == MEM) ? 0 : 1; + regnum = (memnum == 0) ? 1 : 0; + if (!strncmp (i.maxq20_op[regnum].reg->reg_name, "DP", 2) && + !strncmp ((i.maxq20_op[memnum].mem->name) + 1, + i.maxq20_op[regnum].reg->reg_name, 5) + && strcmp ((i.maxq20_op[memnum].mem->name) + 1, + i.maxq20_op[regnum].reg->reg_name)) + { + as_bad (_ + ("Contradictory movement between DP register and memory access using DP")); + return 0; + } + else if (!strcmp (i.maxq20_op[regnum].reg->reg_name, "SP") && + !strncmp ((i.maxq20_op[memnum].mem->name) + 1, + i.maxq20_op[regnum].reg->reg_name, 2)) + { + as_bad (_ + ("SP and @SP-- cannot be used together in a move instruction")); + return 0; + } + } + + /* This filter restricts the instructions containing source and destination + bits to only CTRL module of the serial registers. Peripheral registers + yet to be defined. */ + + if (i.bit_operands == 1 && i.operands == 2) + { + int bitnum = (i.types[0] == BIT) ? 0 : 1; + + if (strcmp (i.maxq20_op[bitnum].r_bit->reg->reg_name, "ACC")) + { + if (i.maxq20_op[bitnum].r_bit->reg->Mod_name >= 0x7 && + i.maxq20_op[bitnum].r_bit->reg->Mod_name != CTRL) + { + as_bad (_ + ("Only Module 8 system registers allowed in this operation")); + return 0; + } + } + } + + /* This filter is for checking the register bits. */ + if (i.bit_operands == 1 || i.operands == 2) + { + int bitnum = 0, size = 0; + + bitnum = (i.types[0] == BIT) ? 0 : 1; + if (i.bit_operands == 1) + { + switch (i.maxq20_op[bitnum].r_bit->reg->rtype) + { + case Reg_8W: + size = 7; /* 8 bit register, both read and write. */ + break; + case Reg_16W: + size = 15; + break; + case Reg_8R: + size = 7; + if (bitnum == 0) + { + as_fatal (_("Read only Register used as destination")); + return 0; + } + break; + + case Reg_16R: + size = 15; + if (bitnum == 0) + { + as_fatal (_("Read only Register used as destination")); + return 0; + } + break; + } + + if (size < (i.maxq20_op[bitnum].r_bit)->bit) + { + as_bad (_("Bit No '%d'exceeds register size in this operation"), + (i.maxq20_op[bitnum].r_bit)->bit); + return 0; + } + } + + if (i.bit_operands == 2) + { + switch ((i.maxq20_op[0].r_bit)->reg->rtype) + { + case Reg_8W: + size = 7; /* 8 bit register, both read and write. */ + break; + case Reg_16W: + size = 15; + break; + case Reg_8R: + case Reg_16R: + as_fatal (_("Read only Register used as destination")); + return 0; + } + + if (size < (i.maxq20_op[0].r_bit)->bit) + { + as_bad (_ + ("Bit No '%d' exceeds register size in this operation"), + (i.maxq20_op[0].r_bit)->bit); + return 0; + } + + size = 0; + switch ((i.maxq20_op[1].r_bit)->reg->rtype) + { + case Reg_8R: + case Reg_8W: + size = 7; /* 8 bit register, both read and write. */ + break; + case Reg_16R: + case Reg_16W: + size = 15; + break; + } + + if (size < (i.maxq20_op[1].r_bit)->bit) + { + as_bad (_ + ("Bit No '%d' exceeds register size in this operation"), + (i.maxq20_op[1].r_bit)->bit); + return 0; + } + } + } + + /* No branch operations should occur into the data memory. Hence any memory + references have to be filtered out when used with instructions like + jump, djnz[] and call. */ + + if (!strcmp (i.op.name, "JUMP") || !strcmp (i.op.name, "CALL") + || !strncmp (i.op.name, "DJNZ", 4)) + { + if (i.mem_operands) + as_warn (_ + ("Memory References cannot be used with branching operations\n")); + } + + if (!strcmp (i.op.name, "DJNZ")) + { + if (! + (strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]") + || strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]"))) + { + as_bad (_("DJNZ uses only LC[n] register \n")); + return 0; + } + } + + /* No destination register used should be read only! */ + if ((i.operands == 2 && i.types[0] == REG) || !strcmp (i.op.name, "POP") + || !strcmp (i.op.name, "POPI")) + { /* The destination is a register */ + int regnum = 0; + + if (!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI")) + { + regnum = 0; + + if (i.types[regnum] == MEM) + { + mem_access_syntax *mem_op = NULL; + + mem_op = + (mem_access_syntax *) hash_find (mem_syntax_hash, + i.maxq20_op[regnum].mem-> + name); + if (mem_op->type == SRC && mem_op) + { + as_bad (_ + ("'%s' operand cant be used as destination in %s"), + mem_op->name, i.op.name); + return 0; + } + } + } + + if (i.maxq20_op[regnum].reg->rtype == Reg_8R + || i.maxq20_op[regnum].reg->rtype == Reg_16R) + { + as_bad (_("Read only register used for writing purposes '%s'"), + i.maxq20_op[regnum].reg->reg_name); + return 0; + } + } + + /* While moving the address of a data in the data section, the destination + should be either data pointers only. */ + if ((i.data_operands) && (i.operands == 2)) + { + if ((i.types[0] != REG) && (i.types[0] != MEM)) + { + as_bad (_("Invalid destination for this kind of source.")); + return 0; + } + + if (i.types[0] == REG && i.maxq20_op[0].reg->rtype == Reg_8W) + { + as_bad (_ + ("Invalid register as destination for this kind of source.Only data pointers can be used.")); + return 0; + } + } + return 1; +} + +static int +decode_insn (void) +{ + /* Check for the format Bit if defined. */ + if (i.op.format == 0 || i.op.format == 1) + i.instr[0] = i.op.format << 7; + else + { + /* Format bit not defined. We will have to be find it out ourselves. */ + if (i.imm_operands == 1 || i.data_operands == 1 || i.disp_operands == 1) + i.op.format = 0; + else + i.op.format = 1; + i.instr[0] = i.op.format << 7; + } + + /* Now for the destination register. */ + + /* If destination register is already defined . The conditions are the + following: (1) The second entry in the destination array should be 0 (2) + If there are two operands then the first entry should not be a register, + memory or a register bit (3) If there are less than two operands and the + it is not a pop operation (4) The second argument is the carry + flag(applicable to move Acc.<b>,C. */ + if (i.op.dst[1] == 0 + && + ((i.types[0] != REG && i.types[0] != MEM && i.types[0] != BIT + && i.operands == 2) || (i.operands < 2 && strcmp (i.op.name, "POP") + && strcmp (i.op.name, "POPI")) + || (i.op.arg[1] == FLAG_C))) + { + i.op.dst[0] &= 0x7f; + i.instr[0] |= i.op.dst[0]; + } + else if (i.op.dst[1] == 0 && !strcmp (i.op.name, "DJNZ") + && + (((i.types[0] == REG) + && (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]") + || !strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]"))))) + { + i.op.dst[0] &= 0x7f; + if (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]")) + i.instr[0] |= 0x4D; + + if (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]")) + i.instr[0] |= 0x5D; + } + else + { + unsigned char temp; + + /* Target register will have to be specified. */ + if (i.types[0] == REG + && (i.op.dst[0] == REG || i.op.dst[0] == (REG | MEM))) + { + temp = (i.maxq20_op[0].reg)->opcode; + temp &= 0x7f; + i.instr[0] |= temp; + } + else if (i.types[0] == MEM && (i.op.dst[0] == (REG | MEM))) + { + temp = (i.maxq20_op[0].mem)->opcode; + temp &= 0x7f; + i.instr[0] |= temp; + } + else if (i.types[0] == BIT && (i.op.dst[0] == REG)) + { + temp = (i.maxq20_op[0].r_bit)->reg->opcode; + temp &= 0x7f; + i.instr[0] |= temp; + } + else if (i.types[1] == BIT && (i.op.dst[0] == BIT)) + { + temp = (i.maxq20_op[1].r_bit)->bit; + temp = temp << 4; + temp |= i.op.dst[1]; + temp &= 0x7f; + i.instr[0] |= temp; + } + else + { + as_bad (_("Invalid Instruction")); + return 0; + } + } + + /* Now for the source register. */ + + /* If Source register is already known. The following conditions are + checked: (1) There are no operands (2) If there is only one operand and + it is a flag (3) If the operation is MOVE C,#0/#1 (4) If it is a POP + operation. */ + + if (i.operands == 0 || (i.operands == 1 && i.types[0] == FLAG) + || (i.types[0] == FLAG && i.types[1] == IMMBIT) + || !strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI")) + i.instr[1] = i.op.src[0]; + + else if (i.imm_operands == 1 && ((i.op.src[0] & IMM) == IMM)) + i.instr[1] = i.maxq20_op[this_operand].imms; + + else if (i.types[this_operand] == REG && ((i.op.src[0] & REG) == REG)) + i.instr[1] = (char) ((i.maxq20_op[this_operand].reg)->opcode); + + else if (i.types[this_operand] == BIT && ((i.op.src[0] & REG) == REG)) + i.instr[1] = (char) (i.maxq20_op[this_operand].r_bit->reg->opcode); + + else if (i.types[this_operand] == MEM && ((i.op.src[0] & MEM) == MEM)) + i.instr[1] = (char) ((i.maxq20_op[this_operand].mem)->opcode); + + else if (i.types[this_operand] == DATA && ((i.op.src[0] & DATA) == DATA)) + /* This will copy only the lower order bytes into the instruction. The + higher order bytes have already been copied into the prefix register. */ + i.instr[1] = 0; + + /* Decoding the source in the case when the second array entry is not 0. + This means that the source register has been divided into two nibbles. */ + + else if (i.op.src[1] != 0) + { + /* If the first operand is a accumulator bit then + the first 4 bits will be filled with the bit number. */ + if (i.types[0] == BIT && ((i.op.src[0] & BIT) == BIT)) + { + unsigned char temp = (i.maxq20_op[0].r_bit)->bit; + + temp = temp << 4; + temp |= i.op.src[1]; + i.instr[1] = temp; + } + /* In case of MOVE dst.<b>,#1 The first nibble in the source register + has to start with a zero. This is called a ZEROBIT */ + else if (i.types[0] == BIT && ((i.op.src[0] & ZEROBIT) == ZEROBIT)) + { + char temp = (i.maxq20_op[0].r_bit)->bit; + + temp = temp << 4; + temp |= i.op.src[1]; + temp &= 0x7f; + i.instr[1] = temp; + } + /* Similarly for a ONEBIT */ + else if (i.types[0] == BIT && ((i.op.src[0] & ONEBIT) == ONEBIT)) + { + char temp = (i.maxq20_op[0].r_bit)->bit; + + temp = temp << 4; + temp |= i.op.src[1]; + temp |= 0x80; + i.instr[1] = temp; + } + /* In case the second operand is a register bit (MOVE C,Acc.<b> or MOVE + C,src.<b> */ + else if (i.types[1] == BIT) + { + if (i.op.src[1] == 0 && i.op.src[1] == REG) + i.instr[1] = (i.maxq20_op[1].r_bit)->reg->opcode; + + else if (i.op.src[0] == BIT && i.op.src) + { + char temp = (i.maxq20_op[1].r_bit)->bit; + + temp = temp << 4; + temp |= i.op.src[1]; + i.instr[1] = temp; + } + } + else + { + as_bad (_("Invalid Instruction")); + return 0; + } + } + return 1; +} + +/* This is a function for outputting displacement operands. */ + +static void +output_disp (fragS *insn_start_frag, offsetT insn_start_off) +{ + char *p; + relax_substateT subtype; + symbolS *sym; + offsetT off; + int diff; + + diff = 0; + insn_start_frag = frag_now; + insn_start_off = frag_now_fix (); + + switch (i.Instr_Prefix) + { + case LONG_PREFIX: + subtype = EXPLICT_LONG_PREFIX; + break; + case SHORT_PREFIX: + subtype = SHORT_PREFIX; + break; + default: + subtype = NO_PREFIX; + break; + } + + /* Its a symbol. Here we end the frag and start the relaxation. Now in our + case there is no need for relaxation. But we do need support for a + prefix operator. Hence we will check whethere is room for 4 bytes ( 2 + for prefix + 2 for the current instruction ) Hence if at a particular + time we find out whether the prefix operator is reqd , we shift the + current instruction two places ahead and insert the prefix instruction. */ + frag_grow (2 + 2); + p = frag_more (2); + + sym = i.maxq20_op[this_operand].disps->X_add_symbol; + off = i.maxq20_op[this_operand].disps->X_add_number; + + if (i.maxq20_op[this_operand].disps->X_add_symbol != NULL && sym && frag_now + && (subtype != EXPLICT_LONG_PREFIX)) + { + /* If in the same frag. */ + if (frag_now == symbol_get_frag (sym)) + { + diff = + ((((expressionS *) symbol_get_value_expression (sym))-> + X_add_number) - insn_start_off); + + diff = diff / MAXQ_OCTETS_PER_BYTE; + + if (diff >= -128 && diff <= 127) + { + i.instr[1] = (char) diff; + + /* This will be overwritten later when the symbol is resolved. */ + *p = i.instr[1]; + *(p + 1) = i.instr[0]; + + /* No Need to create a FIXUP. */ + return; + } + } + } + + /* This will be overwritten later when the symbol is resolved. */ + *p = i.instr[1]; + *(p + 1) = i.instr[0]; + + if (i.maxq20_op[this_operand].disps->X_op != O_constant + && i.maxq20_op[this_operand].disps->X_op != O_symbol) + { + /* Handle complex expressions. */ + sym = make_expr_symbol (i.maxq20_op[this_operand].disps); + off = 0; + } + + /* Vineet : This has been added for md_estimate_size_before_relax to + estimate the correct size. */ + if (subtype != SHORT_PREFIX) + i.reloc[this_operand] = LONG_PREFIX; + + frag_var (rs_machine_dependent, 2, i.reloc[this_operand], subtype, sym, off, p); +} + +/* This is a function for outputting displacement operands. */ + +static void +output_data (fragS *insn_start_frag, offsetT insn_start_off) +{ + char *p; + relax_substateT subtype; + symbolS *sym; + offsetT off; + int diff; + + diff = 0; + off = 0; + insn_start_frag = frag_now; + insn_start_off = frag_now_fix (); + + subtype = EXPLICT_LONG_PREFIX; + + frag_grow (2 + 2); + p = frag_more (2); + + sym = i.maxq20_op[this_operand].data; + off = 0; + + /* This will be overwritten later when the symbol is resolved. */ + *p = i.instr[1]; + *(p + 1) = i.instr[0]; + + if (i.maxq20_op[this_operand].disps->X_op != O_constant + && i.maxq20_op[this_operand].disps->X_op != O_symbol) + /* Handle complex expressions. */ + /* Because data is already in terms of symbol so no + need to convert it from expression to symbol. */ + off = 0; + + frag_var (rs_machine_dependent, 2, i.reloc[this_operand], subtype, sym, off, p); +} + +static void +output_insn (void) +{ + fragS *insn_start_frag; + offsetT insn_start_off; + char *p; + + /* Tie dwarf2 debug info to the address at the start of the insn. We can't + do this after the insn has been output as the current frag may have been + closed off. eg. by frag_var. */ + dwarf2_emit_insn (0); + + /* To ALign the text section on word. */ + + frag_align (1, 0, 1); + + /* We initialise the frags for this particular instruction. */ + insn_start_frag = frag_now; + insn_start_off = frag_now_fix (); + + /* If there are displacement operators(unresolved) present, then handle + them separately. */ + if (i.disp_operands) + { + output_disp (insn_start_frag, insn_start_off); + return; + } + + if (i.data_operands) + { + output_data (insn_start_frag, insn_start_off); + return; + } + + /* Check whether the INSERT_BUFFER has to be written. */ + if (strcmp (INSERT_BUFFER, "")) + { + p = frag_more (2); + + *p++ = INSERT_BUFFER[1]; + *p = INSERT_BUFFER[0]; + } + + /* Check whether the prefix instruction has to be written. */ + if (strcmp (PFX_INSN, "")) + { + p = frag_more (2); + + *p++ = PFX_INSN[1]; + *p = PFX_INSN[0]; + } + + p = frag_more (2); + /* For Little endian. */ + *p++ = i.instr[1]; + *p = i.instr[0]; +} + +static void +make_new_reg_table (void) +{ + unsigned long size_pm = sizeof (peripheral_reg_table); + num_of_reg = ARRAY_SIZE (peripheral_reg_table); + + new_reg_table = xmalloc (size_pm); + if (new_reg_table == NULL) + as_bad (_("Cannot allocate memory")); + + memcpy (new_reg_table, peripheral_reg_table, size_pm); +} + +/* pmmain performs the initilizations for the pheripheral modules. */ + +static void +pmmain (void) +{ + make_new_reg_table (); + return; +} + +void +md_begin (void) +{ + const char *hash_err = NULL; + int c = 0; + char *p; + const MAXQ20_OPCODE_INFO *optab; + MAXQ20_OPCODES *core_optab; /* For opcodes of the same name. This will + be inserted into the hash table. */ + struct reg *reg_tab; + struct mem_access_syntax const *memsyntab; + struct mem_access *memtab; + struct bit_name *bittab; + + /* Initilize pherioipheral modules. */ + pmmain (); + + /* Initialise the opcode hash table. */ + op_hash = hash_new (); + + optab = op_table; /* Initialise it to the first entry of the + maxq20 operand table. */ + + /* Setup for loop. */ + core_optab = xmalloc (sizeof (MAXQ20_OPCODES)); + core_optab->start = optab; + + while (1) + { + ++optab; + if (optab->name == NULL || strcmp (optab->name, (optab - 1)->name) != 0) + { + /* different name --> ship out current template list; add to hash + table; & begin anew. */ + + core_optab->end = optab; +#ifdef MAXQ10S + if (max_version == 10) + { + if (((optab - 1)->arch == MAXQ10) || ((optab - 1)->arch == MAX)) + { + hash_err = hash_insert (op_hash, + (optab - 1)->name, + (PTR) core_optab); + } + } + else if (max_version == 20) + { + /* MAXQ20 */ + if (((optab - 1)->arch == MAXQ20) || ((optab - 1)->arch == MAX)) + { +#endif + hash_err = hash_insert (op_hash, + (optab - 1)->name, + (PTR) core_optab); +#if MAXQ10S + } + } + else + as_fatal (_("Internal Error: Illegal Architecure specified")); +#endif + if (hash_err) + as_fatal (_("Internal Error: Can't hash %s: %s"), + (optab - 1)->name, hash_err); + + if (optab->name == NULL) + break; + core_optab = xmalloc (sizeof (MAXQ20_OPCODES)); + core_optab->start = optab; + } + } + + /* Initialise a new register table. */ + reg_hash = hash_new (); + + for (reg_tab = system_reg_table; + reg_tab < (system_reg_table + ARRAY_SIZE (system_reg_table)); + reg_tab++) + { +#if MAXQ10S + switch (max_version) + { + case 10: /* MAXQ10 */ + if ((reg_tab->arch == MAXQ10) || (reg_tab->arch == MAX)) + hash_err = hash_insert (reg_hash, reg_tab->reg_name, (PTR) reg_tab); + break; + + case 20: /* MAXQ20 */ + if ((reg_tab->arch == MAXQ20) || (reg_tab->arch == MAX)) + { +#endif + hash_err = + hash_insert (reg_hash, reg_tab->reg_name, (PTR) reg_tab); +#if MAXQ10S + } + break; + default: + as_fatal (_("Invalid architecture type")); + } +#endif + + if (hash_err) + as_fatal (_("Internal Error : Can't Hash %s : %s"), + reg_tab->reg_name, hash_err); + } + + /* Pheripheral Registers Entry. */ + for (reg_tab = new_reg_table; + reg_tab < (new_reg_table + num_of_reg - 1); reg_tab++) + { + hash_err = hash_insert (reg_hash, reg_tab->reg_name, (PTR) reg_tab); + + if (hash_err) + as_fatal (_("Internal Error : Can't Hash %s : %s"), + reg_tab->reg_name, hash_err); + } + + /* Initialise a new memory operand table. */ + mem_hash = hash_new (); + + for (memtab = mem_table; + memtab < mem_table + ARRAY_SIZE (mem_table); + memtab++) + { + hash_err = hash_insert (mem_hash, memtab->name, (PTR) memtab); + if (hash_err) + as_fatal (_("Internal Error : Can't Hash %s : %s"), + memtab->name, hash_err); + } + + bit_hash = hash_new (); + + for (bittab = bit_table; + bittab < bit_table + ARRAY_SIZE (bit_table); + bittab++) + { + hash_err = hash_insert (bit_hash, bittab->name, (PTR) bittab); + if (hash_err) + as_fatal (_("Internal Error : Can't Hash %s : %s"), + bittab->name, hash_err); + } + + mem_syntax_hash = hash_new (); + + for (memsyntab = mem_access_syntax_table; + memsyntab < mem_access_syntax_table + ARRAY_SIZE (mem_access_syntax_table); + memsyntab++) + { + hash_err = + hash_insert (mem_syntax_hash, memsyntab->name, (PTR) memsyntab); + if (hash_err) + as_fatal (_("Internal Error : Can't Hash %s : %s"), + memsyntab->name, hash_err); + } + + /* Initialise the lexical tables,mnemonic chars,operand chars. */ + for (c = 0; c < 256; c++) + { + if (ISDIGIT (c)) + { + digit_chars[c] = c; + mnemonic_chars[c] = c; + operand_chars[c] = c; + register_chars[c] = c; + } + else if (ISLOWER (c)) + { + mnemonic_chars[c] = c; + operand_chars[c] = c; + register_chars[c] = c; + } + else if (ISUPPER (c)) + { + mnemonic_chars[c] = TOLOWER (c); + register_chars[c] = c; + operand_chars[c] = c; + } + + if (ISALPHA (c) || ISDIGIT (c)) + { + identifier_chars[c] = c; + } + else if (c > 128) + { + identifier_chars[c] = c; + operand_chars[c] = c; + } + } + + /* All the special characters. */ + register_chars['@'] = '@'; + register_chars['+'] = '+'; + register_chars['-'] = '-'; + digit_chars['-'] = '-'; + identifier_chars['_'] = '_'; + identifier_chars['.'] = '.'; + register_chars['['] = '['; + register_chars[']'] = ']'; + operand_chars['_'] = '_'; + operand_chars['#'] = '#'; + mnemonic_chars['['] = '['; + mnemonic_chars[']'] = ']'; + + for (p = operand_special_chars; *p != '\0'; p++) + operand_chars[(unsigned char) *p] = (unsigned char) *p; +} + +/* md_assemble - Parse Instr - Seprate menmonics and operands - lookup the + menmunonic in the operand table - Parse operands and populate the + structure/template - Match the operand with opcode and its validity - + Output Instr. */ + +void +md_assemble (char *line) +{ + int j; + + char mnemonic[MAX_MNEM_SIZE]; + char temp4prev[256]; + static char prev_insn[256]; + + /* Initialize globals. */ + memset (&i, '\0', sizeof (i)); + for (j = 0; j < MAX_OPERANDS; j++) + i.reloc[j] = NO_RELOC; + + i.prefix = -1; + PFX_INSN[0] = 0; + PFX_INSN[1] = 0; + INSERT_BUFFER[0] = 0; + INSERT_BUFFER[1] = 0; + + memcpy (temp4prev, line, strlen (line) + 1); + + save_stack_p = save_stack; + + line = (char *) parse_insn (line, mnemonic); + if (line == NULL) + return; + + line = (char *) parse_operands (line, mnemonic); + if (line == NULL) + return; + + /* Next, we find a template that matches the given insn, making sure the + overlap of the given operands types is consistent with the template + operand types. */ + if (!match_template ()) + return; + + /* In the MAXQ20, there are certain register combinations, and other + restrictions which are not allowed. We will try to resolve these right + now. */ + if (!match_filters ()) + return; + + /* Check for the approprate PFX register. */ + set_prefix (); + pfx_for_imm_val (0); + + if (!decode_insn ()) /* decode insn. */ + need_pass_2 = 1; + + /* Check for Exlipct PFX instruction. */ + if (PFX_INSN[0] && (strstr (prev_insn, "PFX") || strstr (prev_insn, "pfx"))) + as_warn (_("Ineffective insntruction %s \n"), prev_insn); + + memcpy (prev_insn, temp4prev, strlen (temp4prev) + 1); + + /* We are ready to output the insn. */ + output_insn (); +} diff --git a/gas/config/tc-maxq.h b/gas/config/tc-maxq.h new file mode 100644 index 0000000..73ba9a9 --- /dev/null +++ b/gas/config/tc-maxq.h @@ -0,0 +1,172 @@ +/* tc-maxq.h -- Header file for the asssembler(MAXQ) + + Copyright 2004 Free Software Foundation, Inc. + + Contributed by HCL Technologies Pvt. Ltd. + + Written by Vineet Sharma(vineets@noida.hcltech.com) Inderpreet + S.(inderpreetb@noida.hcltech.com) + + This file is part of GAS. + + 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. */ + +#ifndef _TC_MAXQ_H_ +#define _TC_MAXQ_H_ + +#ifndef NO_RELOC +#define NO_RELOC 0 +#endif + +/* `md_short_jump_size' `md_long_jump_size' `md_create_short_jump' + `md_create_long_jump' If `WORKING_DOT_WORD' is defined, GAS will not do + broken word processing (*note Broken words::.). Otherwise, you should set + `md_short_jump_size' to the size of a short jump (a jump that is just long + enough to jump around a long jmp) and `md_long_jump_size' to the size of a + long jump (a jump that can go anywhere in the function), You should define + `md_create_short_jump' to create a short jump around a long jump, and + define `md_create_long_jump' to create a long jump. */ +#define WORKING_DOT_WORD +typedef enum _RELOC_ENUM +{ + MAXQ_WORDDATA = 5, /* Word+n. */ + MAXQ_LONGDATA = 2, /* Long+n. */ + MAXQ_INTERSEGMENT = 4, /* Text to any other segment. */ + MAXQ_SHORTJUMP = BFD_RELOC_16_PCREL_S2, /* PC Relative. */ + MAXQ_LONGJUMP = 6, /* Absolute Jump. */ + EXTERNAL_RELOC = 8, + INTERSEGMENT_RELOC +} +RELOC_ENUM; + +#ifndef NEED_FX_R_TYPE +#define NEED_FX_R_TYPE /* For defining our own rloc type. */ +#endif + +#ifndef MAX_STACK +#define MAX_STACK 0xf +#endif + +#ifndef TC_MAXQ20 +#define TC_MAXQ20 1 +#endif + +#ifndef MAX_OPERAND_SIZE +#define MAX_OPERAND_SIZE 255 +#endif + +#ifndef MAXQ_INSTRUCTION_SIZE +#define MAXQ_INSTRUCTION_SIZE 2 /* 16 - BITS */ +#endif + +#if MAXQ_INSTRUCTION_SIZE +#define MAXQ_OCTETS_PER_BYTE MAXQ_INSTRUCTION_SIZE +#else +#define MAXQ_OCTETS_PER_BYTE OCTETS_PER_BYTE +#endif + +/* if this macro is defined gas will use this instead of comment_chars. */ +#define tc_comments_chars maxq20_comment_chars + +#define tc_coff_symbol_emit_hook(a) ; /* not used */ + +#define md_section_align(SEGMENT, SIZE) (SIZE) + +/* Locally defined symbol shoudnot be adjusted to section symbol. */ +#define tc_fix_adjustable(FIX) 0 + +#define TC_COFF_SIZEMACHDEP(frag) tc_coff_sizemachdep (frag) +extern int tc_coff_sizemachdep (fragS * frag); + +/* This specifies that the target has been defined as little endian - + default. */ +#define TARGET_BYTES_BIG_ENDIAN 0 + +#define MAX_MEM_NAME_SIZE 12 +#define MAX_REG_NAME_SIZE 7 +#define MAX_MNEM_SIZE 8 + +#define END_OF_INSN '\0' + +/* This macro is the BFD archetectureto pass to 'bfd_set_arch_mach'. */ +#define TARGET_ARCH bfd_arch_maxq + +/* This macro is the BFD machine number to pass to 'bfd_set_arch_mach'. + If not defines GAS will use 0. */ +#define TARGET_MACH maxq20_mach () +extern unsigned long maxq20_mach (void); + +/* COFF format. */ +#ifndef BFD_ASSEMBLER + +/* Defines the magic number we are using for the maxq-coff. defined in coff.h. */ +#define COFF_MAGIC MAXQ20MAGIC +#define BFD_ARCH bfd_arch_maxq + +/* Whether the reloc should be outputted to the file or not. This + is only true if the symbol we are relocating for is not empty. */ +#define TC_COUNT_RELOC(fixp) ((fixp) -> fx_addsy != NULL) + +/* This macro translates between an internal fix and an coff reloc type. */ +#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype (fixP) +extern short tc_coff_fix2rtype (struct fix *); + +#endif /* BFD_ASSEMBLER */ + +#ifndef LEX_AT +/* We define this macro to generate a fixup for a data allocation pseudo-op. */ +#define TC_CONS_FIX_NEW(FRAG,OFF,LEN,EXP) maxq20_cons_fix_new (FRAG,OFF,LEN,EXP) +extern void maxq20_cons_fix_new (fragS *, unsigned int, unsigned int, expressionS *); +#endif + +/* Define md_number_to_chars as the appropriate standard big endian or This + should just call either `number_to_chars_bigendian' or + `number_to_chars_littleendian', whichever is appropriate. On targets like + the MIPS which support options to change the endianness, which function to + call is a runtime decision. On other targets, `md_number_to_chars' can be + a simple macro. */ +#define md_number_to_chars maxq_number_to_chars +extern void maxq_number_to_chars (char *, valueT, int); + +/* If this macro is defined, it is a pointer to a NULL terminated list of + chracters which may appear in an operand. GAS already assumes that all + alphanumeric chracters, and '$', '.', and '_' may appear in an + operand("symbol_char"in app.c). This macro may be defined to treat + additional chracters as appearing in an operand. This affects the way in + which GAS removes whitespaces before passing the string to md_assemble. */ +#define tc_symbol_chars_extra_symbol_chars + +/* Define away the call to md_operand in the expression parsing code. This is + called whenever the expression parser can't parse the input and gives the + assembler backend a chance to deal with it instead. */ +#define md_operand(x) + +#define MAX_OPERANDS 2 /* Max operands per instruction. */ +#define MAX_IMMEDIATE_OPERANDS 1 /* Max immediate operands per instruction. */ +#define MAX_MEMORY_OPERANDS 1 /* Max memory operands per instruction. */ + +/* Define the prefix we are using while trying to use an immediate value in + an instruction. e.g move A[0], #03h. */ +#define IMMEDIATE_PREFIX '#' + +#define ABSOLUTE_PREFIX '@' + +/* This here defines the opcode of the nop operation on the MAXQ. We did + declare it here when we tried to fill the align bites with nop's but GAS + only expects nop's to be single byte instruction. */ +#define NOP_OPCODE (char)0xDA3A + +#define SIZE_OF_PM sizeof(pmodule) /* Size of the structure. */ + +#endif /* TC_MAXQ_H */ |