| /* tc-maxq.c -- assembler code for a MAXQ chip. |
| |
| Copyright 2004, 2005, 2006, 2007, 2008 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 3, 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, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, 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 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 = bfd_mach_maxq20; |
| |
| 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. */ |
| |
| /* 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 |
| |
| /* 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; |
| } |
| |
| static void |
| maxq_target (int target) |
| { |
| max_version = target; |
| bfd_set_arch_mach (stdoutput, bfd_arch_maxq, max_version); |
| } |
| |
| 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 = bfd_mach_maxq10; |
| break; |
| case MAXQ_20: |
| max_version = bfd_mach_maxq20; |
| 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\ |
| ")); |
| } |
| |
| 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); |
| gas_assert (rel->howto != NULL); |
| } |
| |
| return rel; |
| } |
| |
| /* 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; |
| |
| /* PC stores the value of the next instruction. */ |
| diff = (call_addr - instr) - 1; |
| |
| 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; |
| } |
| |
| char * |
| md_atof (int type, char * litP, int * sizeP) |
| { |
| if (type == 'd') |
| /* The size of Double has been changed to 2 words ie 32 bits. */ |
| type = 'f'; |
| return ieee_md_atof (type, litP, sizeP, FALSE); |
| } |
| |
| 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; |
| } |
| |
| /* GAS will call this for every rs_machine_dependent fragment. The |
| instruction is completed using the data from the relaxation pass. It may |
| also create any necessary relocations. */ |
| void |
| md_convert_frag (bfd * headers ATTRIBUTE_UNUSED, |
| segT seg ATTRIBUTE_UNUSED, |
| fragS * fragP) |
| { |
| 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); |
| |
| /* PC points to the next Instruction. */ |
| displacement_from_opcode_start = ((target_address - opcode_address) - 1); |
| |
| if ((displacement_from_opcode_start >= -128 |
| && displacement_from_opcode_start <= 127) |
| && (fragP->fr_subtype == SHORT_PREFIX |
| || fragP->fr_subtype == NO_PREFIX)) |
| { |
| /* Its a displacement. */ |
| *opcode = (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; |
| 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; |
| |
| 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_fix returns. |
| If the overflow check is relevant for the target machine, then |
| 'md_apply_fix' should modify 'valueT *val', typically to the value stored |
| in the object file (not to be done in MAXQ). */ |
| |
| void |
| md_apply_fix (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); |
| } |
| |
| /* Remember value for tc_gen_reloc. */ |
| fixP->fx_addnumber = *valT; |
| } |
| |
| /* 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. */ |
| {"maxq10", maxq_target, bfd_mach_maxq10}, |
| {"maxq20", maxq_target, bfd_mach_maxq20}, |
| {NULL, 0, 0}, |
| }; |
| |
| #define SET_PFX_ARG(x) (PFX_INSN[1] = x) |
| |
| |
| /* This function sets the PFX value corresponding 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 Character. */ |
| 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) != bfd_und_section_ptr) |
| { |
| /* 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 (0); |
| 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 |
| && OUTPUT_FLAVOR == bfd_target_aout_flavour |
| && exp_seg != absolute_section |
| && exp_seg != text_section |
| && exp_seg != data_section |
| && exp_seg != bss_section && exp_seg != undefined_section |
| && !bfd_is_com_section (exp_seg)) |
| { |
| as_bad (_("unimplemented segment %s in operand"), exp_seg->name); |
| 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 original 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); |
| |
| /* PC points to the next instruction. */ |
| diff = (diff / MAXQ_OCTETS_PER_BYTE) - 1; |
| |
| 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 == bfd_mach_maxq10) |
| { |
| if (((optab - 1)->arch == MAXQ10) || ((optab - 1)->arch == MAX)) |
| { |
| hash_err = hash_insert (op_hash, |
| (optab - 1)->name, |
| (void *) core_optab); |
| } |
| } |
| else if (max_version == bfd_mach_maxq20) |
| { |
| if (((optab - 1)->arch == MAXQ20) || ((optab - 1)->arch == MAX)) |
| { |
| #endif |
| hash_err = hash_insert (op_hash, |
| (optab - 1)->name, |
| (void *) 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 bfd_mach_maxq10: |
| if ((reg_tab->arch == MAXQ10) || (reg_tab->arch == MAX)) |
| hash_err = hash_insert (reg_hash, reg_tab->reg_name, (void *) reg_tab); |
| break; |
| |
| case bfd_mach_maxq20: |
| if ((reg_tab->arch == MAXQ20) || (reg_tab->arch == MAX)) |
| { |
| #endif |
| hash_err = |
| hash_insert (reg_hash, reg_tab->reg_name, (void *) 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, (void *) 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, (void *) 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, (void *) 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, (void *) 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; |
| |
| /* Set the maxq arch type. */ |
| maxq_target (max_version); |
| } |
| |
| /* 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 appropriate 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 (); |
| } |