Add support for the AVR Tiny series of microcontrollers.
[deliverable/binutils-gdb.git] / gas / config / tc-avr.c
index e4bc59c8bc3ccba74a70c1210cc483c2c0818491..9ed7de8a54cebae4da34afccdaa72216d1dc8561 100644 (file)
@@ -89,6 +89,7 @@ static struct mcu_type_s mcu_types[] =
   {"avrxmega5",  AVR_ISA_XMEGA,   bfd_mach_avrxmega5},
   {"avrxmega6",  AVR_ISA_XMEGA,   bfd_mach_avrxmega6},
   {"avrxmega7",  AVR_ISA_XMEGA,   bfd_mach_avrxmega7},
+  {"avrtiny",    AVR_ISA_AVRTINY, bfd_mach_avrtiny},
   {"at90s1200",  AVR_ISA_1200,    bfd_mach_avr1},
   {"attiny11",   AVR_ISA_AVR1,    bfd_mach_avr1},
   {"attiny12",   AVR_ISA_AVR1,    bfd_mach_avr1},
@@ -323,6 +324,12 @@ static struct mcu_type_s mcu_types[] =
   {"atxmega128a1", AVR_ISA_XMEGA, bfd_mach_avrxmega7},
   {"atxmega128a1u", AVR_ISA_XMEGAU, bfd_mach_avrxmega7},
   {"atxmega128a4u", AVR_ISA_XMEGAU, bfd_mach_avrxmega7},
+  {"attiny4",      AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny5",      AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny9",      AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny10",     AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny20",     AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+  {"attiny40",     AVR_ISA_AVRTINY, bfd_mach_avrtiny},
   {NULL, 0, 0}
 };
 
@@ -513,7 +520,7 @@ md_show_usage (FILE *stream)
        "                   avrxmega5 - XMEGA, > 64K, <= 128K FLASH, > 64K RAM\n"
        "                   avrxmega6 - XMEGA, > 128K, <= 256K FLASH, <= 64K RAM\n"
        "                   avrxmega7 - XMEGA, > 128K, <= 256K FLASH, > 64K RAM\n"
-       "                   or immediate microcontroller name.\n"));
+       "                   avrtiny   - AVR Tiny core with 16 gp registers\n"));
   fprintf (stream,
       _("  -mall-opcodes    accept all AVR opcodes, even if not supported by MCU\n"
        "  -mno-skip-bug    disable warnings for skipping two-word instructions\n"
@@ -855,29 +862,41 @@ avr_operand (struct avr_opcodes_s *opcode,
     case 'a':
     case 'v':
       if (*str == 'r' || *str == 'R')
-       {
-         char r_name[20];
+        {
+          char r_name[20];
 
-         str = extract_word (str, r_name, sizeof (r_name));
-         op_mask = 0xff;
-         if (ISDIGIT (r_name[1]))
-           {
-             if (r_name[2] == '\0')
-               op_mask = r_name[1] - '0';
-             else if (r_name[1] != '0'
-                      && ISDIGIT (r_name[2])
-                      && r_name[3] == '\0')
-               op_mask = (r_name[1] - '0') * 10 + r_name[2] - '0';
-           }
-       }
+          str = extract_word (str, r_name, sizeof (r_name));
+          op_mask = 0xff;
+          if (ISDIGIT (r_name[1]))
+            {
+              if (r_name[2] == '\0')
+                op_mask = r_name[1] - '0';
+              else if (r_name[1] != '0'
+                       && ISDIGIT (r_name[2])
+                       && r_name[3] == '\0')
+                op_mask = (r_name[1] - '0') * 10 + r_name[2] - '0';
+            }
+        }
       else
-       {
-         op_mask = avr_get_constant (str, 31);
-         str = input_line_pointer;
-       }
+        {
+          op_mask = avr_get_constant (str, 31);
+          str = input_line_pointer;
+        }
+
+      if (avr_mcu->mach == bfd_mach_avrtiny)
+        {
+          if (op_mask < 16 || op_mask > 31)
+            {
+              as_bad (_("register name or number from 16 to 31 required"));
+              break;
+            }
+        }
+      else if (op_mask > 31)
+        {
+          as_bad (_("register name or number from 0 to 31 required"));
+          break;
+        }
 
-      if (op_mask <= 31)
-       {
          switch (*op)
            {
            case 'a':
@@ -905,9 +924,6 @@ avr_operand (struct avr_opcodes_s *opcode,
              break;
            }
          break;
-       }
-      as_bad (_("register name or number from 0 to 31 required"));
-      break;
 
     case 'e':
       {
@@ -1014,6 +1030,12 @@ avr_operand (struct avr_opcodes_s *opcode,
                   &op_expr, FALSE, BFD_RELOC_16);
       break;
 
+    case 'j':
+      str = parse_exp (str, &op_expr);
+      fix_new_exp (frag_now, where, opcode->insn_size * 2,
+                  &op_expr, FALSE, BFD_RELOC_AVR_LDS_STS_16);
+      break;
+
     case 'M':
       {
        bfd_reloc_code_real_type r_type;
@@ -1415,11 +1437,21 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where);
          break;
 
+       case BFD_RELOC_AVR_LDS_STS_16:
+         if ((value < 0x40) || (value > 0xBF))
+           as_warn_where (fixP->fx_file, fixP->fx_line,
+                          _("operand out of range: 0x%lx"),
+                          (unsigned long)value);
+         insn |= ((value & 0xF) | ((value & 0x30) << 5) | ((value & 0x40) << 2));
+         bfd_putl16 ((bfd_vma) insn, where);
+         break;
+
        case BFD_RELOC_AVR_6:
          if ((value > 63) || (value < 0))
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("operand out of range: %ld"), value);
-         bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7) | ((value & (1 << 5)) << 8)), where);
+         bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7)
+                                       | ((value & (1 << 5)) << 8)), where);
          break;
 
        case BFD_RELOC_AVR_6_ADIW:
@@ -1594,6 +1626,28 @@ md_assemble (char *str)
 
   opcode = (struct avr_opcodes_s *) hash_find (avr_hash, op);
 
+  if (opcode && !avr_opt.all_opcodes)
+    {
+      /* Check if the instruction's ISA bit is ON in the ISA bits of the part 
+         specified by the user.  If not look for other instructions
+        specifications with same mnemonic who's ISA bits matches. 
+
+         This requires include/opcode/avr.h to have the instructions with
+         same mnenomic to be specified in sequence.  */
+
+      while ((opcode->isa & avr_mcu->isa) != opcode->isa)
+        {
+          opcode++;
+     
+          if (opcode->name && strcmp(op, opcode->name))
+            {
+              as_bad (_("illegal opcode %s for mcu %s"), 
+                      opcode->name, avr_mcu->name);
+              return;
+            }
+        }
+    } 
+
   if (opcode == NULL)
     {
       as_bad (_("unknown opcode `%s'"), op);
@@ -1606,9 +1660,6 @@ md_assemble (char *str)
   if (*str && *opcode->constraints == '?')
     ++opcode;
 
-  if (!avr_opt.all_opcodes && (opcode->isa & avr_mcu->isa) != opcode->isa)
-    as_bad (_("illegal opcode %s for mcu %s"), opcode->name, avr_mcu->name);
-
   dwarf2_emit_insn (0);
 
   /* We used to set input_line_pointer to the result of get_operands,
This page took 0.025634 seconds and 4 git commands to generate.