This commit was generated by cvs2svn to track changes on a CVS vendor
[platform/upstream/binutils.git] / opcodes / pdp11-dis.c
1 /* Print DEC PDP-11 instructions.
2    Copyright 2001, 2002, 2004, 2005 Free Software Foundation, Inc.
3
4    This file is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
17    MA 02110-1301, USA.  */
18
19 #include "sysdep.h"
20 #include "dis-asm.h"
21 #include "opcode/pdp11.h"
22
23 #define AFTER_INSTRUCTION       "\t"
24 #define OPERAND_SEPARATOR       ", "
25
26 #define JUMP    0x1000  /* Flag that this operand is used in a jump.  */
27
28 #define FPRINTF (*info->fprintf_func)
29 #define F       info->stream
30
31 /* Sign-extend a 16-bit number in an int.  */
32 #define SIGN_BITS       (8 * sizeof (int) - 16)
33 #define sign_extend(x) (((x) << SIGN_BITS) >> SIGN_BITS)
34
35 static int
36 read_word (bfd_vma memaddr, int *word, disassemble_info *info)
37 {
38   int status;
39   bfd_byte x[2];
40
41   status = (*info->read_memory_func) (memaddr, x, 2, info);
42   if (status != 0)
43     return -1;
44
45   *word = x[1] << 8 | x[0];
46   return 0;
47 }
48
49 static void
50 print_signed_octal (int n, disassemble_info *info)
51 {
52   if (n < 0)
53     FPRINTF (F, "-%o", -n);
54   else
55     FPRINTF (F, "%o", n);
56 }
57
58 static void
59 print_reg (int reg, disassemble_info *info)
60 {
61   /* Mask off the addressing mode, if any.  */
62   reg &= 7;
63
64   switch (reg)
65     {
66     case 0: case 1: case 2: case 3: case 4: case 5:
67                 FPRINTF (F, "r%d", reg); break;
68     case 6:     FPRINTF (F, "sp"); break;
69     case 7:     FPRINTF (F, "pc"); break;
70     default: ;  /* error */
71     }
72 }
73
74 static void
75 print_freg (int freg, disassemble_info *info)
76 {
77   FPRINTF (F, "fr%d", freg);
78 }
79
80 static int
81 print_operand (bfd_vma *memaddr, int code, disassemble_info *info)
82 {
83   int mode = (code >> 3) & 7;
84   int reg = code & 7;
85   int disp;
86
87   switch (mode)
88     {
89     case 0:
90       print_reg (reg, info);
91       break;
92     case 1:
93       FPRINTF (F, "(");
94       print_reg (reg, info);
95       FPRINTF (F, ")");
96       break;
97     case 2:
98       if (reg == 7)
99         {
100           int data;
101
102           if (read_word (*memaddr, &data, info) < 0)
103             return -1;
104           FPRINTF (F, "$");
105           print_signed_octal (sign_extend (data), info);
106           *memaddr += 2;
107         }
108       else
109         {
110           FPRINTF (F, "(");
111           print_reg (reg, info);
112           FPRINTF (F, ")+");
113         }
114         break;
115     case 3:
116       if (reg == 7)
117         {
118           int address;
119
120           if (read_word (*memaddr, &address, info) < 0)
121             return -1;
122           FPRINTF (F, "*$%o", address);
123           *memaddr += 2;
124         }
125       else
126         {
127           FPRINTF (F, "*(");
128           print_reg (reg, info);
129           FPRINTF (F, ")+");
130         }
131         break;
132     case 4:
133       FPRINTF (F, "-(");
134       print_reg (reg, info);
135       FPRINTF (F, ")");
136       break;
137     case 5:
138       FPRINTF (F, "*-(");
139       print_reg (reg, info);
140       FPRINTF (F, ")");
141       break;
142     case 6:
143     case 7:
144       if (read_word (*memaddr, &disp, info) < 0)
145         return -1;
146       *memaddr += 2;
147       if (reg == 7)
148         {
149           bfd_vma address = *memaddr + sign_extend (disp);
150
151           if (mode == 7)
152             FPRINTF (F, "*");
153           if (!(code & JUMP))
154             FPRINTF (F, "$");
155           (*info->print_address_func) (address, info);
156         }
157       else
158         {
159           if (mode == 7)
160             FPRINTF (F, "*");
161           print_signed_octal (sign_extend (disp), info);
162           FPRINTF (F, "(");
163           print_reg (reg, info);
164           FPRINTF (F, ")");
165         }
166       break;
167     }
168
169   return 0;
170 }
171
172 static int
173 print_foperand (bfd_vma *memaddr, int code, disassemble_info *info)
174 {
175   int mode = (code >> 3) & 7;
176   int reg = code & 7;
177
178   if (mode == 0)
179     print_freg (reg, info);
180   else
181     return print_operand (memaddr, code, info);
182
183   return 0;
184 }
185
186 /* Print the PDP-11 instruction at address MEMADDR in debugged memory,
187    on INFO->STREAM.  Returns length of the instruction, in bytes.  */
188
189 int
190 print_insn_pdp11 (bfd_vma memaddr, disassemble_info *info)
191 {
192   bfd_vma start_memaddr = memaddr;
193   int opcode;
194   int src, dst;
195   int i;
196
197   info->bytes_per_line = 6;
198   info->bytes_per_chunk = 2;
199   info->display_endian = BFD_ENDIAN_LITTLE;
200
201   if (read_word (memaddr, &opcode, info) != 0)
202     return -1;
203   memaddr += 2;
204
205   src = (opcode >> 6) & 0x3f;
206   dst = opcode & 0x3f;
207
208   for (i = 0; i < pdp11_num_opcodes; i++)
209     {
210 #define OP pdp11_opcodes[i]
211       if ((opcode & OP.mask) == OP.opcode)
212         switch (OP.type)
213           {
214           case PDP11_OPCODE_NO_OPS:
215             FPRINTF (F, OP.name);
216             goto done;
217           case PDP11_OPCODE_REG:
218             FPRINTF (F, OP.name);
219             FPRINTF (F, AFTER_INSTRUCTION);
220             print_reg (dst, info);
221             goto done;
222           case PDP11_OPCODE_OP:
223             FPRINTF (F, OP.name);
224             FPRINTF (F, AFTER_INSTRUCTION);
225             if (strcmp (OP.name, "jmp") == 0)
226               dst |= JUMP;
227             if (print_operand (&memaddr, dst, info) < 0)
228               return -1;
229             goto done;
230           case PDP11_OPCODE_FOP:
231             FPRINTF (F, OP.name);
232             FPRINTF (F, AFTER_INSTRUCTION);
233             if (strcmp (OP.name, "jmp") == 0)
234               dst |= JUMP;
235             if (print_foperand (&memaddr, dst, info) < 0)
236               return -1;
237             goto done;
238           case PDP11_OPCODE_REG_OP:
239             FPRINTF (F, OP.name);
240             FPRINTF (F, AFTER_INSTRUCTION);
241             print_reg (src, info);
242             FPRINTF (F, OPERAND_SEPARATOR);
243             if (strcmp (OP.name, "jsr") == 0)
244               dst |= JUMP;
245             if (print_operand (&memaddr, dst, info) < 0)
246               return -1;
247             goto done;
248           case PDP11_OPCODE_REG_OP_REV:
249             FPRINTF (F, OP.name);
250             FPRINTF (F, AFTER_INSTRUCTION);
251             if (print_operand (&memaddr, dst, info) < 0)
252               return -1;
253             FPRINTF (F, OPERAND_SEPARATOR);
254             print_reg (src, info);
255             goto done;
256           case PDP11_OPCODE_AC_FOP:
257             {
258               int ac = (opcode & 0xe0) >> 6;
259               FPRINTF (F, OP.name);
260               FPRINTF (F, AFTER_INSTRUCTION);
261               print_freg (ac, info);
262               FPRINTF (F, OPERAND_SEPARATOR);
263               if (print_foperand (&memaddr, dst, info) < 0)
264                 return -1;
265               goto done;
266             }
267           case PDP11_OPCODE_FOP_AC:
268             {
269               int ac = (opcode & 0xe0) >> 6;
270               FPRINTF (F, OP.name);
271               FPRINTF (F, AFTER_INSTRUCTION);
272               if (print_foperand (&memaddr, dst, info) < 0)
273                 return -1;
274               FPRINTF (F, OPERAND_SEPARATOR);
275               print_freg (ac, info);
276               goto done;
277             }
278           case PDP11_OPCODE_AC_OP:
279             {
280               int ac = (opcode & 0xe0) >> 6;
281               FPRINTF (F, OP.name);
282               FPRINTF (F, AFTER_INSTRUCTION);
283               print_freg (ac, info);
284               FPRINTF (F, OPERAND_SEPARATOR);
285               if (print_operand (&memaddr, dst, info) < 0)
286                 return -1;
287               goto done;
288             }
289           case PDP11_OPCODE_OP_AC:
290             {
291               int ac = (opcode & 0xe0) >> 6;
292               FPRINTF (F, OP.name);
293               FPRINTF (F, AFTER_INSTRUCTION);
294               if (print_operand (&memaddr, dst, info) < 0)
295                 return -1;
296               FPRINTF (F, OPERAND_SEPARATOR);
297               print_freg (ac, info);
298               goto done;
299             }
300           case PDP11_OPCODE_OP_OP:
301             FPRINTF (F, OP.name);
302             FPRINTF (F, AFTER_INSTRUCTION);
303             if (print_operand (&memaddr, src, info) < 0)
304               return -1;
305             FPRINTF (F, OPERAND_SEPARATOR);
306             if (print_operand (&memaddr, dst, info) < 0)
307               return -1;
308             goto done;
309           case PDP11_OPCODE_DISPL:
310             {
311               int displ = (opcode & 0xff) << 8;
312               bfd_vma address = memaddr + (sign_extend (displ) >> 7);
313               FPRINTF (F, OP.name);
314               FPRINTF (F, AFTER_INSTRUCTION);
315               (*info->print_address_func) (address, info);
316               goto done;
317             }
318           case PDP11_OPCODE_REG_DISPL:
319             {
320               int displ = (opcode & 0x3f) << 10;
321               bfd_vma address = memaddr - (displ >> 9);
322
323               FPRINTF (F, OP.name);
324               FPRINTF (F, AFTER_INSTRUCTION);
325               print_reg (src, info);
326               FPRINTF (F, OPERAND_SEPARATOR);
327               (*info->print_address_func) (address, info);
328               goto done;
329             }
330           case PDP11_OPCODE_IMM8:
331             {
332               int code = opcode & 0xff;
333               FPRINTF (F, OP.name);
334               FPRINTF (F, AFTER_INSTRUCTION);
335               FPRINTF (F, "%o", code);
336               goto done;
337             }
338           case PDP11_OPCODE_IMM6:
339             {
340               int code = opcode & 0x3f;
341               FPRINTF (F, OP.name);
342               FPRINTF (F, AFTER_INSTRUCTION);
343               FPRINTF (F, "%o", code);
344               goto done;
345             }
346           case PDP11_OPCODE_IMM3:
347             {
348               int code = opcode & 7;
349               FPRINTF (F, OP.name);
350               FPRINTF (F, AFTER_INSTRUCTION);
351               FPRINTF (F, "%o", code);
352               goto done;
353             }
354           case PDP11_OPCODE_ILLEGAL:
355             {
356               FPRINTF (F, ".word");
357               FPRINTF (F, AFTER_INSTRUCTION);
358               FPRINTF (F, "%o", opcode);
359               goto done;
360             }
361           default:
362             /* TODO: is this a proper way of signalling an error? */
363             FPRINTF (F, "<internal error: unrecognized instruction type>");
364             return -1;
365           }
366 #undef OP
367     }
368  done:
369
370   return memaddr - start_memaddr;
371 }