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