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
read_word(bfd_vma memaddr,int * word,disassemble_info * info)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
print_signed_octal(int n,disassemble_info * info)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
print_reg(int reg,disassemble_info * info)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
print_freg(int freg,disassemble_info * info)75 print_freg (int freg, disassemble_info *info)
76 {
77   FPRINTF (F, "fr%d", freg);
78 }
79 
80 static int
print_operand(bfd_vma * memaddr,int code,disassemble_info * info)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
print_foperand(bfd_vma * memaddr,int code,disassemble_info * info)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
print_insn_pdp11(bfd_vma memaddr,disassemble_info * info)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 }
372