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