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
35static int
36read_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
49static void
50print_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
58static void
59print_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
74static void
75print_freg (int freg, disassemble_info *info)
76{
77  FPRINTF (F, "fr%d", freg);
78}
79
80static int
81print_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
172static int
173print_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
189int
190print_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}
372