Add GNU Free Documentation License
[binutils.git] / opcodes / cgen-ibld.in
blob51032934a7a997f3df0b1e5f880b62028d674662
1 /* Instruction building/extraction support for @arch@. -*- C -*-
3 THIS FILE IS MACHINE GENERATED WITH CGEN: Cpu tools GENerator.
4 - the resultant file is machine generated, cgen-ibld.in isn't
6 Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
8 This file is part of the GNU Binutils and GDB, the GNU debugger.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software Foundation, Inc.,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
24 /* ??? Eventually more and more of this stuff can go to cpu-independent files.
25    Keep that in mind.  */
27 #include "sysdep.h"
28 #include <ctype.h>
29 #include <stdio.h>
30 #include "ansidecl.h"
31 #include "dis-asm.h"
32 #include "bfd.h"
33 #include "symcat.h"
34 #include "@prefix@-desc.h"
35 #include "@prefix@-opc.h"
36 #include "opintl.h"
38 #undef min
39 #define min(a,b) ((a) < (b) ? (a) : (b))
40 #undef max
41 #define max(a,b) ((a) > (b) ? (a) : (b))
43 /* Used by the ifield rtx function.  */
44 #define FLD(f) (fields->f)
46 static const char * insert_normal
47      PARAMS ((CGEN_CPU_DESC, long, unsigned int, unsigned int, unsigned int,
48               unsigned int, unsigned int, unsigned int, CGEN_INSN_BYTES_PTR));
49 static const char * insert_insn_normal
50      PARAMS ((CGEN_CPU_DESC, const CGEN_INSN *,
51               CGEN_FIELDS *, CGEN_INSN_BYTES_PTR, bfd_vma));
53 static int extract_normal
54      PARAMS ((CGEN_CPU_DESC, CGEN_EXTRACT_INFO *, CGEN_INSN_INT,
55               unsigned int, unsigned int, unsigned int, unsigned int,
56               unsigned int, unsigned int, bfd_vma, long *));
57 static int extract_insn_normal
58      PARAMS ((CGEN_CPU_DESC, const CGEN_INSN *, CGEN_EXTRACT_INFO *,
59               CGEN_INSN_INT, CGEN_FIELDS *, bfd_vma));
60 static void put_insn_int_value
61      PARAMS ((CGEN_CPU_DESC, CGEN_INSN_BYTES_PTR, int, int, CGEN_INSN_INT));
64 /* Operand insertion.  */
66 #if ! CGEN_INT_INSN_P
68 /* Subroutine of insert_normal.  */
70 static CGEN_INLINE void
71 insert_1 (cd, value, start, length, word_length, bufp)
72      CGEN_CPU_DESC cd;
73      unsigned long value;
74      int start,length,word_length;
75      unsigned char *bufp;
77   unsigned long x,mask;
78   int shift;
79   int big_p = CGEN_CPU_INSN_ENDIAN (cd) == CGEN_ENDIAN_BIG;
81   switch (word_length)
82     {
83     case 8:
84       x = *bufp;
85       break;
86     case 16:
87       if (big_p)
88         x = bfd_getb16 (bufp);
89       else
90         x = bfd_getl16 (bufp);
91       break;
92     case 24:
93       /* ??? This may need reworking as these cases don't necessarily
94          want the first byte and the last two bytes handled like this.  */
95       if (big_p)
96         x = (bufp[0] << 16) | bfd_getb16 (bufp + 1);
97       else
98         x = bfd_getl16 (bufp) | (bufp[2] << 16);
99       break;
100     case 32:
101       if (big_p)
102         x = bfd_getb32 (bufp);
103       else
104         x = bfd_getl32 (bufp);
105       break;
106     default :
107       abort ();
108     }
110   /* Written this way to avoid undefined behaviour.  */
111   mask = (((1L << (length - 1)) - 1) << 1) | 1;
112   if (CGEN_INSN_LSB0_P)
113     shift = (start + 1) - length;
114   else
115     shift = (word_length - (start + length));
116   x = (x & ~(mask << shift)) | ((value & mask) << shift);
118   switch (word_length)
119     {
120     case 8:
121       *bufp = x;
122       break;
123     case 16:
124       if (big_p)
125         bfd_putb16 (x, bufp);
126       else
127         bfd_putl16 (x, bufp);
128       break;
129     case 24:
130       /* ??? This may need reworking as these cases don't necessarily
131          want the first byte and the last two bytes handled like this.  */
132       if (big_p)
133         {
134           bufp[0] = x >> 16;
135           bfd_putb16 (x, bufp + 1);
136         }
137       else
138         {
139           bfd_putl16 (x, bufp);
140           bufp[2] = x >> 16;
141         }
142       break;
143     case 32:
144       if (big_p)
145         bfd_putb32 (x, bufp);
146       else
147         bfd_putl32 (x, bufp);
148       break;
149     default :
150       abort ();
151     }
154 #endif /* ! CGEN_INT_INSN_P */
156 /* Default insertion routine.
158    ATTRS is a mask of the boolean attributes.
159    WORD_OFFSET is the offset in bits from the start of the insn of the value.
160    WORD_LENGTH is the length of the word in bits in which the value resides.
161    START is the starting bit number in the word, architecture origin.
162    LENGTH is the length of VALUE in bits.
163    TOTAL_LENGTH is the total length of the insn in bits.
165    The result is an error message or NULL if success.  */
167 /* ??? This duplicates functionality with bfd's howto table and
168    bfd_install_relocation.  */
169 /* ??? This doesn't handle bfd_vma's.  Create another function when
170    necessary.  */
172 static const char *
173 insert_normal (cd, value, attrs, word_offset, start, length, word_length,
174                total_length, buffer)
175      CGEN_CPU_DESC cd;
176      long value;
177      unsigned int attrs;
178      unsigned int word_offset, start, length, word_length, total_length;
179      CGEN_INSN_BYTES_PTR buffer;
181   static char errbuf[100];
182   /* Written this way to avoid undefined behaviour.  */
183   unsigned long mask = (((1L << (length - 1)) - 1) << 1) | 1;
185   /* If LENGTH is zero, this operand doesn't contribute to the value.  */
186   if (length == 0)
187     return NULL;
189 #if 0
190   if (CGEN_INT_INSN_P
191       && word_offset != 0)
192     abort ();
193 #endif
195   if (word_length > 32)
196     abort ();
198   /* For architectures with insns smaller than the base-insn-bitsize,
199      word_length may be too big.  */
200   if (cd->min_insn_bitsize < cd->base_insn_bitsize)
201     {
202       if (word_offset == 0
203           && word_length > total_length)
204         word_length = total_length;
205     }
207   /* Ensure VALUE will fit.  */
208   if (! CGEN_BOOL_ATTR (attrs, CGEN_IFLD_SIGNED))
209     {
210       unsigned long maxval = mask;
211       
212       if ((unsigned long) value > maxval)
213         {
214           /* xgettext:c-format */
215           sprintf (errbuf,
216                    _("operand out of range (%lu not between 0 and %lu)"),
217                    value, maxval);
218           return errbuf;
219         }
220     }
221   else
222     {
223       if (! cgen_signed_overflow_ok_p (cd))
224         {
225           long minval = - (1L << (length - 1));
226           long maxval =   (1L << (length - 1)) - 1;
227           
228           if (value < minval || value > maxval)
229             {
230               sprintf
231                 /* xgettext:c-format */
232                 (errbuf, _("operand out of range (%ld not between %ld and %ld)"),
233                  value, minval, maxval);
234               return errbuf;
235             }
236         }
237     }
239 #if CGEN_INT_INSN_P
241   {
242     int shift;
244     if (CGEN_INSN_LSB0_P)
245       shift = (word_offset + start + 1) - length;
246     else
247       shift = total_length - (word_offset + start + length);
248     *buffer = (*buffer & ~(mask << shift)) | ((value & mask) << shift);
249   }
251 #else /* ! CGEN_INT_INSN_P */
253   {
254     unsigned char *bufp = (unsigned char *) buffer + word_offset / 8;
256     insert_1 (cd, value, start, length, word_length, bufp);
257   }
259 #endif /* ! CGEN_INT_INSN_P */
261   return NULL;
264 /* Default insn builder (insert handler).
265    The instruction is recorded in CGEN_INT_INSN_P byte order
266    (meaning that if CGEN_INT_INSN_P BUFFER is an int * and thus the value is
267    recorded in host byte order, otherwise BUFFER is an array of bytes and the
268    value is recorded in target byte order).
269    The result is an error message or NULL if success.  */
271 static const char *
272 insert_insn_normal (cd, insn, fields, buffer, pc)
273      CGEN_CPU_DESC cd;
274      const CGEN_INSN * insn;
275      CGEN_FIELDS * fields;
276      CGEN_INSN_BYTES_PTR buffer;
277      bfd_vma pc;
279   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
280   unsigned long value;
281   const unsigned char * syn;
283   CGEN_INIT_INSERT (cd);
284   value = CGEN_INSN_BASE_VALUE (insn);
286   /* If we're recording insns as numbers (rather than a string of bytes),
287      target byte order handling is deferred until later.  */
289 #if CGEN_INT_INSN_P
291   put_insn_int_value (cd, buffer, cd->base_insn_bitsize,
292                       CGEN_FIELDS_BITSIZE (fields), value);
294 #else
296   cgen_put_insn_value (cd, buffer, min (cd->base_insn_bitsize,
297                                         CGEN_FIELDS_BITSIZE (fields)),
298                        value);
300 #endif /* ! CGEN_INT_INSN_P */
302   /* ??? It would be better to scan the format's fields.
303      Still need to be able to insert a value based on the operand though;
304      e.g. storing a branch displacement that got resolved later.
305      Needs more thought first.  */
307   for (syn = CGEN_SYNTAX_STRING (syntax); * syn != '\0'; ++ syn)
308     {
309       const char *errmsg;
311       if (CGEN_SYNTAX_CHAR_P (* syn))
312         continue;
314       errmsg = (* cd->insert_operand) (cd, CGEN_SYNTAX_FIELD (*syn),
315                                        fields, buffer, pc);
316       if (errmsg)
317         return errmsg;
318     }
320   return NULL;
323 /* Cover function to store an insn value into an integral insn.  Must go here
324  because it needs <prefix>-desc.h for CGEN_INT_INSN_P.  */
326 static void
327 put_insn_int_value (cd, buf, length, insn_length, value)
328      CGEN_CPU_DESC cd;
329      CGEN_INSN_BYTES_PTR buf;
330      int length;
331      int insn_length;
332      CGEN_INSN_INT value;
334   /* For architectures with insns smaller than the base-insn-bitsize,
335      length may be too big.  */
336   if (length > insn_length)
337     *buf = value;
338   else
339     {
340       int shift = insn_length - length;
341       /* Written this way to avoid undefined behaviour.  */
342       CGEN_INSN_INT mask = (((1L << (length - 1)) - 1) << 1) | 1;
343       *buf = (*buf & ~(mask << shift)) | ((value & mask) << shift);
344     }
347 /* Operand extraction.  */
349 #if ! CGEN_INT_INSN_P
351 /* Subroutine of extract_normal.
352    Ensure sufficient bytes are cached in EX_INFO.
353    OFFSET is the offset in bytes from the start of the insn of the value.
354    BYTES is the length of the needed value.
355    Returns 1 for success, 0 for failure.  */
357 static CGEN_INLINE int
358 fill_cache (cd, ex_info, offset, bytes, pc)
359      CGEN_CPU_DESC cd;
360      CGEN_EXTRACT_INFO *ex_info;
361      int offset, bytes;
362      bfd_vma pc;
364   /* It's doubtful that the middle part has already been fetched so
365      we don't optimize that case.  kiss.  */
366   int mask;
367   disassemble_info *info = (disassemble_info *) ex_info->dis_info;
369   /* First do a quick check.  */
370   mask = (1 << bytes) - 1;
371   if (((ex_info->valid >> offset) & mask) == mask)
372     return 1;
374   /* Search for the first byte we need to read.  */
375   for (mask = 1 << offset; bytes > 0; --bytes, ++offset, mask <<= 1)
376     if (! (mask & ex_info->valid))
377       break;
379   if (bytes)
380     {
381       int status;
383       pc += offset;
384       status = (*info->read_memory_func)
385         (pc, ex_info->insn_bytes + offset, bytes, info);
387       if (status != 0)
388         {
389           (*info->memory_error_func) (status, pc, info);
390           return 0;
391         }
393       ex_info->valid |= ((1 << bytes) - 1) << offset;
394     }
396   return 1;
399 /* Subroutine of extract_normal.  */
401 static CGEN_INLINE long
402 extract_1 (cd, ex_info, start, length, word_length, bufp, pc)
403      CGEN_CPU_DESC cd;
404      CGEN_EXTRACT_INFO *ex_info;
405      int start,length,word_length;
406      unsigned char *bufp;
407      bfd_vma pc;
409   unsigned long x,mask;
410   int shift;
411   int big_p = CGEN_CPU_INSN_ENDIAN (cd) == CGEN_ENDIAN_BIG;
413   switch (word_length)
414     {
415     case 8:
416       x = *bufp;
417       break;
418     case 16:
419       if (big_p)
420         x = bfd_getb16 (bufp);
421       else
422         x = bfd_getl16 (bufp);
423       break;
424     case 24:
425       /* ??? This may need reworking as these cases don't necessarily
426          want the first byte and the last two bytes handled like this.  */
427       if (big_p)
428         x = (bufp[0] << 16) | bfd_getb16 (bufp + 1);
429       else
430         x = bfd_getl16 (bufp) | (bufp[2] << 16);
431       break;
432     case 32:
433       if (big_p)
434         x = bfd_getb32 (bufp);
435       else
436         x = bfd_getl32 (bufp);
437       break;
438     default :
439       abort ();
440     }
442   /* Written this way to avoid undefined behaviour.  */
443   mask = (((1L << (length - 1)) - 1) << 1) | 1;
444   if (CGEN_INSN_LSB0_P)
445     shift = (start + 1) - length;
446   else
447     shift = (word_length - (start + length));
448   return (x >> shift) & mask;
451 #endif /* ! CGEN_INT_INSN_P */
453 /* Default extraction routine.
455    INSN_VALUE is the first base_insn_bitsize bits of the insn in host order,
456    or sometimes less for cases like the m32r where the base insn size is 32
457    but some insns are 16 bits.
458    ATTRS is a mask of the boolean attributes.  We only need `SIGNED',
459    but for generality we take a bitmask of all of them.
460    WORD_OFFSET is the offset in bits from the start of the insn of the value.
461    WORD_LENGTH is the length of the word in bits in which the value resides.
462    START is the starting bit number in the word, architecture origin.
463    LENGTH is the length of VALUE in bits.
464    TOTAL_LENGTH is the total length of the insn in bits.
466    Returns 1 for success, 0 for failure.  */
468 /* ??? The return code isn't properly used.  wip.  */
470 /* ??? This doesn't handle bfd_vma's.  Create another function when
471    necessary.  */
473 static int
474 extract_normal (cd, ex_info, insn_value, attrs, word_offset, start, length,
475                 word_length, total_length, pc, valuep)
476      CGEN_CPU_DESC cd;
477 #if ! CGEN_INT_INSN_P
478      CGEN_EXTRACT_INFO *ex_info;
479 #else
480      CGEN_EXTRACT_INFO *ex_info ATTRIBUTE_UNUSED;
481 #endif
482      CGEN_INSN_INT insn_value;
483      unsigned int attrs;
484      unsigned int word_offset, start, length, word_length, total_length;
485 #if ! CGEN_INT_INSN_P
486      bfd_vma pc;
487 #else
488      bfd_vma pc ATTRIBUTE_UNUSED;
489 #endif
490      long *valuep;
492   CGEN_INSN_INT value;
494   /* If LENGTH is zero, this operand doesn't contribute to the value
495      so give it a standard value of zero.  */
496   if (length == 0)
497     {
498       *valuep = 0;
499       return 1;
500     }
502 #if 0
503   if (CGEN_INT_INSN_P
504       && word_offset != 0)
505     abort ();
506 #endif
508   if (word_length > 32)
509     abort ();
511   /* For architectures with insns smaller than the insn-base-bitsize,
512      word_length may be too big.  */
513   if (cd->min_insn_bitsize < cd->base_insn_bitsize)
514     {
515       if (word_offset == 0
516           && word_length > total_length)
517         word_length = total_length;
518     }
520   /* Does the value reside in INSN_VALUE?  */
522   if (CGEN_INT_INSN_P || word_offset == 0)
523     {
524       /* Written this way to avoid undefined behaviour.  */
525       CGEN_INSN_INT mask = (((1L << (length - 1)) - 1) << 1) | 1;
527       if (CGEN_INSN_LSB0_P)
528         value = insn_value >> ((word_offset + start + 1) - length);
529       else
530         value = insn_value >> (total_length - ( word_offset + start + length));
531       value &= mask;
532       /* sign extend? */
533       if (CGEN_BOOL_ATTR (attrs, CGEN_IFLD_SIGNED)
534           && (value & (1L << (length - 1))))
535         value |= ~mask;
536     }
538 #if ! CGEN_INT_INSN_P
540   else
541     {
542       unsigned char *bufp = ex_info->insn_bytes + word_offset / 8;
544       if (word_length > 32)
545         abort ();
547       if (fill_cache (cd, ex_info, word_offset / 8, word_length / 8, pc) == 0)
548         return 0;
550       value = extract_1 (cd, ex_info, start, length, word_length, bufp, pc);
551     }
553 #endif /* ! CGEN_INT_INSN_P */
555   *valuep = value;
557   return 1;
560 /* Default insn extractor.
562    INSN_VALUE is the first base_insn_bitsize bits, translated to host order.
563    The extracted fields are stored in FIELDS.
564    EX_INFO is used to handle reading variable length insns.
565    Return the length of the insn in bits, or 0 if no match,
566    or -1 if an error occurs fetching data (memory_error_func will have
567    been called).  */
569 static int
570 extract_insn_normal (cd, insn, ex_info, insn_value, fields, pc)
571      CGEN_CPU_DESC cd;
572      const CGEN_INSN *insn;
573      CGEN_EXTRACT_INFO *ex_info;
574      CGEN_INSN_INT insn_value;
575      CGEN_FIELDS *fields;
576      bfd_vma pc;
578   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
579   const unsigned char *syn;
581   CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
583   CGEN_INIT_EXTRACT (cd);
585   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
586     {
587       int length;
589       if (CGEN_SYNTAX_CHAR_P (*syn))
590         continue;
592       length = (* cd->extract_operand) (cd, CGEN_SYNTAX_FIELD (*syn),
593                                         ex_info, insn_value, fields, pc);
594       if (length <= 0)
595         return length;
596     }
598   /* We recognized and successfully extracted this insn.  */
599   return CGEN_INSN_BITSIZE (insn);
602 /* machine generated code added here */