1 /* Print DEC PDP-11 instructions.
2    Copyright (C) 2001-2024 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 
36 static int
read_word(bfd_vma memaddr,int * word,disassemble_info * info)37 read_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 
50 static void
print_signed_octal(int n,disassemble_info * info)51 print_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 
59 static void
print_reg(int reg,disassemble_info * info)60 print_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 
75 static void
print_freg(int freg,disassemble_info * info)76 print_freg (int freg, disassemble_info *info)
77 {
78   FPRINTF (F, "fr%d", freg);
79 }
80 
81 static int
print_operand(bfd_vma * memaddr,int code,disassemble_info * info)82 print_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 
173 static int
print_foperand(bfd_vma * memaddr,int code,disassemble_info * info)174 print_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 
190 int
print_insn_pdp11(bfd_vma memaddr,disassemble_info * info)191 print_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