1 /* Disassemble MSP430 instructions.
2    Copyright (C) 2002, 2004, 2005, 2007, 2009, 2010, 2012
3    Free Software Foundation, Inc.
4 
5    Contributed by Dmitry Diky <diwil@mail.ru>
6 
7    This file is part of the GNU opcodes library.
8 
9    This library is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3, or (at your option)
12    any later version.
13 
14    It is distributed in the hope that it will be useful, but WITHOUT
15    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17    License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
22    MA 02110-1301, USA.  */
23 
24 #include "sysdep.h"
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 
29 #include "dis-asm.h"
30 #include "opintl.h"
31 #include "libiberty.h"
32 
33 #define DASM_SECTION
34 #include "opcode/msp430.h"
35 #undef DASM_SECTION
36 
37 
38 #define PS(x)   (0xffff & (x))
39 
40 static unsigned short
msp430dis_opcode(bfd_vma addr,disassemble_info * info)41 msp430dis_opcode (bfd_vma addr, disassemble_info *info)
42 {
43   bfd_byte buffer[2];
44   int status;
45 
46   status = info->read_memory_func (addr, buffer, 2, info);
47   if (status != 0)
48     {
49       info->memory_error_func (status, addr, info);
50       return -1;
51     }
52   return bfd_getl16 (buffer);
53 }
54 
55 static int
msp430_nooperands(struct msp430_opcode_s * opcode,bfd_vma addr ATTRIBUTE_UNUSED,unsigned short insn ATTRIBUTE_UNUSED,char * comm,int * cycles)56 msp430_nooperands (struct msp430_opcode_s *opcode,
57 		   bfd_vma addr ATTRIBUTE_UNUSED,
58 		   unsigned short insn ATTRIBUTE_UNUSED,
59 		   char *comm,
60 		   int *cycles)
61 {
62   /* Pop with constant.  */
63   if (insn == 0x43b2)
64     return 0;
65   if (insn == opcode->bin_opcode)
66     return 2;
67 
68   if (opcode->fmt == 0)
69     {
70       if ((insn & 0x0f00) != 3 || (insn & 0x0f00) != 2)
71 	return 0;
72 
73       strcpy (comm, "emulated...");
74       *cycles = 1;
75     }
76   else
77     {
78       strcpy (comm, "return from interupt");
79       *cycles = 5;
80     }
81 
82   return 2;
83 }
84 
85 static int
msp430_singleoperand(disassemble_info * info,struct msp430_opcode_s * opcode,bfd_vma addr,unsigned short insn,char * op,char * comm,int * cycles)86 msp430_singleoperand (disassemble_info *info,
87 		      struct msp430_opcode_s *opcode,
88 		      bfd_vma addr,
89 		      unsigned short insn,
90 		      char *op,
91 		      char *comm,
92 		      int *cycles)
93 {
94   int regs = 0, regd = 0;
95   int ad = 0, as = 0;
96   int where = 0;
97   int cmd_len = 2;
98   short dst = 0;
99 
100   regd = insn & 0x0f;
101   regs = (insn & 0x0f00) >> 8;
102   as = (insn & 0x0030) >> 4;
103   ad = (insn & 0x0080) >> 7;
104 
105   switch (opcode->fmt)
106     {
107     case 0:			/* Emulated work with dst register.  */
108       if (regs != 2 && regs != 3 && regs != 1)
109 	return 0;
110 
111       /* Check if not clr insn.  */
112       if (opcode->bin_opcode == 0x4300 && (ad || as))
113 	return 0;
114 
115       /* Check if really inc, incd insns.  */
116       if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3)
117 	return 0;
118 
119       if (ad == 0)
120 	{
121 	  *cycles = 1;
122 
123 	  /* Register.  */
124 	  if (regd == 0)
125 	    {
126 	      *cycles += 1;
127 	      sprintf (op, "r0");
128 	    }
129 	  else if (regd == 1)
130 	    sprintf (op, "r1");
131 
132 	  else if (regd == 2)
133 	    sprintf (op, "r2");
134 
135 	  else
136 	    sprintf (op, "r%d", regd);
137 	}
138       else	/* ad == 1 msp430dis_opcode.  */
139 	{
140 	  if (regd == 0)
141 	    {
142 	      /* PC relative.  */
143 	      dst = msp430dis_opcode (addr + 2, info);
144 	      cmd_len += 2;
145 	      *cycles = 4;
146 	      sprintf (op, "0x%04x", dst);
147 	      sprintf (comm, "PC rel. abs addr 0x%04x",
148 		       PS ((short) (addr + 2) + dst));
149 	    }
150 	  else if (regd == 2)
151 	    {
152 	      /* Absolute.  */
153 	      dst = msp430dis_opcode (addr + 2, info);
154 	      cmd_len += 2;
155 	      *cycles = 4;
156 	      sprintf (op, "&0x%04x", PS (dst));
157 	    }
158 	  else
159 	    {
160 	      dst = msp430dis_opcode (addr + 2, info);
161 	      cmd_len += 2;
162 	      *cycles = 4;
163 	      sprintf (op, "%d(r%d)", dst, regd);
164 	    }
165 	}
166       break;
167 
168     case 2:	/* rrc, push, call, swpb, rra, sxt, push, call, reti etc...  */
169       if (as == 0)
170 	{
171 	  if (regd == 3)
172 	    {
173 	      /* Constsnts.  */
174 	      sprintf (op, "#0");
175 	      sprintf (comm, "r3 As==00");
176 	    }
177 	  else
178 	    {
179 	      /* Register.  */
180 	      sprintf (op, "r%d", regd);
181 	    }
182 	  *cycles = 1;
183 	}
184       else if (as == 2)
185 	{
186 	  *cycles = 1;
187 	  if (regd == 2)
188 	    {
189 	      sprintf (op, "#4");
190 	      sprintf (comm, "r2 As==10");
191 	    }
192 	  else if (regd == 3)
193 	    {
194 	      sprintf (op, "#2");
195 	      sprintf (comm, "r3 As==10");
196 	    }
197 	  else
198 	    {
199 	      *cycles = 3;
200 	      /* Indexed register mode @Rn.  */
201 	      sprintf (op, "@r%d", regd);
202 	    }
203 	}
204       else if (as == 3)
205 	{
206 	  *cycles = 1;
207 	  if (regd == 2)
208 	    {
209 	      sprintf (op, "#8");
210 	      sprintf (comm, "r2 As==11");
211 	    }
212 	  else if (regd == 3)
213 	    {
214 	      sprintf (op, "#-1");
215 	      sprintf (comm, "r3 As==11");
216 	    }
217 	  else if (regd == 0)
218 	    {
219 	      *cycles = 3;
220 	      /* absolute. @pc+ */
221 	      dst = msp430dis_opcode (addr + 2, info);
222 	      cmd_len += 2;
223 	      sprintf (op, "#%d", dst);
224 	      sprintf (comm, "#0x%04x", PS (dst));
225 	    }
226 	  else
227 	    {
228 	      *cycles = 3;
229 	      sprintf (op, "@r%d+", regd);
230 	    }
231 	}
232       else if (as == 1)
233 	{
234 	  *cycles = 4;
235 	  if (regd == 0)
236 	    {
237 	      /* PC relative.  */
238 	      dst = msp430dis_opcode (addr + 2, info);
239 	      cmd_len += 2;
240 	      sprintf (op, "0x%04x", PS (dst));
241 	      sprintf (comm, "PC rel. 0x%04x",
242 		       PS ((short) addr + 2 + dst));
243 	    }
244 	  else if (regd == 2)
245 	    {
246 	      /* Absolute.  */
247 	      dst = msp430dis_opcode (addr + 2, info);
248 	      cmd_len += 2;
249 	      sprintf (op, "&0x%04x", PS (dst));
250 	    }
251 	  else if (regd == 3)
252 	    {
253 	      *cycles = 1;
254 	      sprintf (op, "#1");
255 	      sprintf (comm, "r3 As==01");
256 	    }
257 	  else
258 	    {
259 	      /* Indexd.  */
260 	      dst = msp430dis_opcode (addr + 2, info);
261 	      cmd_len += 2;
262 	      sprintf (op, "%d(r%d)", dst, regd);
263 	    }
264 	}
265       break;
266 
267     case 3:			/* Jumps.  */
268       where = insn & 0x03ff;
269       if (where & 0x200)
270 	where |= ~0x03ff;
271       if (where > 512 || where < -511)
272 	return 0;
273 
274       where *= 2;
275       sprintf (op, "$%+-8d", where + 2);
276       sprintf (comm, "abs 0x%x", PS ((short) (addr) + 2 + where));
277       *cycles = 2;
278       return 2;
279       break;
280     default:
281       cmd_len = 0;
282     }
283 
284   return cmd_len;
285 }
286 
287 static int
msp430_doubleoperand(disassemble_info * info,struct msp430_opcode_s * opcode,bfd_vma addr,unsigned short insn,char * op1,char * op2,char * comm1,char * comm2,int * cycles)288 msp430_doubleoperand (disassemble_info *info,
289 		      struct msp430_opcode_s *opcode,
290 		      bfd_vma addr,
291 		      unsigned short insn,
292 		      char *op1,
293 		      char *op2,
294 		      char *comm1,
295 		      char *comm2,
296 		      int *cycles)
297 {
298   int regs = 0, regd = 0;
299   int ad = 0, as = 0;
300   int cmd_len = 2;
301   short dst = 0;
302 
303   regd = insn & 0x0f;
304   regs = (insn & 0x0f00) >> 8;
305   as = (insn & 0x0030) >> 4;
306   ad = (insn & 0x0080) >> 7;
307 
308   if (opcode->fmt == 0)
309     {
310       /* Special case: rla and rlc are the only 2 emulated instructions that
311 	 fall into two operand instructions.  */
312       /* With dst, there are only:
313 	 Rm       	Register,
314          x(Rm)     	Indexed,
315          0xXXXX    	Relative,
316          &0xXXXX    	Absolute
317          emulated_ins   dst
318          basic_ins      dst, dst.  */
319 
320       if (regd != regs || as != ad)
321 	return 0;		/* May be 'data' section.  */
322 
323       if (ad == 0)
324 	{
325 	  /* Register mode.  */
326 	  if (regd == 3)
327 	    {
328 	      strcpy (comm1, _("Illegal as emulation instr"));
329 	      return -1;
330 	    }
331 
332 	  sprintf (op1, "r%d", regd);
333 	  *cycles = 1;
334 	}
335       else			/* ad == 1 */
336 	{
337 	  if (regd == 0)
338 	    {
339 	      /* PC relative, Symbolic.  */
340 	      dst = msp430dis_opcode (addr + 2, info);
341 	      cmd_len += 4;
342 	      *cycles = 6;
343 	      sprintf (op1, "0x%04x", PS (dst));
344 	      sprintf (comm1, "PC rel. 0x%04x",
345 		       PS ((short) addr + 2 + dst));
346 
347 	    }
348 	  else if (regd == 2)
349 	    {
350 	      /* Absolute.  */
351 	      dst = msp430dis_opcode (addr + 2, info);
352 	      /* If the 'src' field is not the same as the dst
353 		 then this is not an rla instruction.  */
354 	      if (dst != msp430dis_opcode (addr + 4, info))
355 		return 0;
356 	      cmd_len += 4;
357 	      *cycles = 6;
358 	      sprintf (op1, "&0x%04x", PS (dst));
359 	    }
360 	  else
361 	    {
362 	      /* Indexed.  */
363 	      dst = msp430dis_opcode (addr + 2, info);
364 	      cmd_len += 4;
365 	      *cycles = 6;
366 	      sprintf (op1, "%d(r%d)", dst, regd);
367 	    }
368 	}
369 
370       *op2 = 0;
371       *comm2 = 0;
372       return cmd_len;
373     }
374 
375   /* Two operands exactly.  */
376   if (ad == 0 && regd == 3)
377     {
378       /* R2/R3 are illegal as dest: may be data section.  */
379       strcpy (comm1, _("Illegal as 2-op instr"));
380       return -1;
381     }
382 
383   /* Source.  */
384   if (as == 0)
385     {
386       *cycles = 1;
387       if (regs == 3)
388 	{
389 	  /* Constsnts.  */
390 	  sprintf (op1, "#0");
391 	  sprintf (comm1, "r3 As==00");
392 	}
393       else
394 	{
395 	  /* Register.  */
396 	  sprintf (op1, "r%d", regs);
397 	}
398     }
399   else if (as == 2)
400     {
401       *cycles = 1;
402 
403       if (regs == 2)
404 	{
405 	  sprintf (op1, "#4");
406 	  sprintf (comm1, "r2 As==10");
407 	}
408       else if (regs == 3)
409 	{
410 	  sprintf (op1, "#2");
411 	  sprintf (comm1, "r3 As==10");
412 	}
413       else
414 	{
415 	  *cycles = 2;
416 
417 	  /* Indexed register mode @Rn.  */
418 	  sprintf (op1, "@r%d", regs);
419 	}
420       if (!regs)
421 	*cycles = 3;
422     }
423   else if (as == 3)
424     {
425       if (regs == 2)
426 	{
427 	  sprintf (op1, "#8");
428 	  sprintf (comm1, "r2 As==11");
429 	  *cycles = 1;
430 	}
431       else if (regs == 3)
432 	{
433 	  sprintf (op1, "#-1");
434 	  sprintf (comm1, "r3 As==11");
435 	  *cycles = 1;
436 	}
437       else if (regs == 0)
438 	{
439 	  *cycles = 3;
440 	  /* Absolute. @pc+.  */
441 	  dst = msp430dis_opcode (addr + 2, info);
442 	  cmd_len += 2;
443 	  sprintf (op1, "#%d", dst);
444 	  sprintf (comm1, "#0x%04x", PS (dst));
445 	}
446       else
447 	{
448 	  *cycles = 2;
449 	  sprintf (op1, "@r%d+", regs);
450 	}
451     }
452   else if (as == 1)
453     {
454       if (regs == 0)
455 	{
456 	  *cycles = 4;
457 	  /* PC relative.  */
458 	  dst = msp430dis_opcode (addr + 2, info);
459 	  cmd_len += 2;
460 	  sprintf (op1, "0x%04x", PS (dst));
461 	  sprintf (comm1, "PC rel. 0x%04x",
462 		   PS ((short) addr + 2 + dst));
463 	}
464       else if (regs == 2)
465 	{
466 	  *cycles = 2;
467 	  /* Absolute.  */
468 	  dst = msp430dis_opcode (addr + 2, info);
469 	  cmd_len += 2;
470 	  sprintf (op1, "&0x%04x", PS (dst));
471 	  sprintf (comm1, "0x%04x", PS (dst));
472 	}
473       else if (regs == 3)
474 	{
475 	  *cycles = 1;
476 	  sprintf (op1, "#1");
477 	  sprintf (comm1, "r3 As==01");
478 	}
479       else
480 	{
481 	  *cycles = 3;
482 	  /* Indexed.  */
483 	  dst = msp430dis_opcode (addr + 2, info);
484 	  cmd_len += 2;
485 	  sprintf (op1, "%d(r%d)", dst, regs);
486 	}
487     }
488 
489   /* Destination. Special care needed on addr + XXXX.  */
490 
491   if (ad == 0)
492     {
493       /* Register.  */
494       if (regd == 0)
495 	{
496 	  *cycles += 1;
497 	  sprintf (op2, "r0");
498 	}
499       else if (regd == 1)
500 	sprintf (op2, "r1");
501 
502       else if (regd == 2)
503 	sprintf (op2, "r2");
504 
505       else
506 	sprintf (op2, "r%d", regd);
507     }
508   else	/* ad == 1.  */
509     {
510       * cycles += 3;
511 
512       if (regd == 0)
513 	{
514 	  /* PC relative.  */
515 	  *cycles += 1;
516 	  dst = msp430dis_opcode (addr + cmd_len, info);
517 	  sprintf (op2, "0x%04x", PS (dst));
518 	  sprintf (comm2, "PC rel. 0x%04x",
519 		   PS ((short) addr + cmd_len + dst));
520 	  cmd_len += 2;
521 	}
522       else if (regd == 2)
523 	{
524 	  /* Absolute.  */
525 	  dst = msp430dis_opcode (addr + cmd_len, info);
526 	  cmd_len += 2;
527 	  sprintf (op2, "&0x%04x", PS (dst));
528 	}
529       else
530 	{
531 	  dst = msp430dis_opcode (addr + cmd_len, info);
532 	  cmd_len += 2;
533 	  sprintf (op2, "%d(r%d)", dst, regd);
534 	}
535     }
536 
537   return cmd_len;
538 }
539 
540 static int
msp430_branchinstr(disassemble_info * info,struct msp430_opcode_s * opcode ATTRIBUTE_UNUSED,bfd_vma addr ATTRIBUTE_UNUSED,unsigned short insn,char * op1,char * comm1,int * cycles)541 msp430_branchinstr (disassemble_info *info,
542 		    struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED,
543 		    bfd_vma addr ATTRIBUTE_UNUSED,
544 		    unsigned short insn,
545 		    char *op1,
546 		    char *comm1,
547 		    int *cycles)
548 {
549   int regs = 0, regd = 0;
550   int as = 0;
551   int cmd_len = 2;
552   short dst = 0;
553 
554   regd = insn & 0x0f;
555   regs = (insn & 0x0f00) >> 8;
556   as = (insn & 0x0030) >> 4;
557 
558   if (regd != 0)	/* Destination register is not a PC.  */
559     return 0;
560 
561   /* dst is a source register.  */
562   if (as == 0)
563     {
564       /* Constants.  */
565       if (regs == 3)
566 	{
567 	  *cycles = 1;
568 	  sprintf (op1, "#0");
569 	  sprintf (comm1, "r3 As==00");
570 	}
571       else
572 	{
573 	  /* Register.  */
574 	  *cycles = 1;
575 	  sprintf (op1, "r%d", regs);
576 	}
577     }
578   else if (as == 2)
579     {
580       if (regs == 2)
581 	{
582 	  *cycles = 2;
583 	  sprintf (op1, "#4");
584 	  sprintf (comm1, "r2 As==10");
585 	}
586       else if (regs == 3)
587 	{
588 	  *cycles = 1;
589 	  sprintf (op1, "#2");
590 	  sprintf (comm1, "r3 As==10");
591 	}
592       else
593 	{
594 	  /* Indexed register mode @Rn.  */
595 	  *cycles = 2;
596 	  sprintf (op1, "@r%d", regs);
597 	}
598     }
599   else if (as == 3)
600     {
601       if (regs == 2)
602 	{
603 	  *cycles = 1;
604 	  sprintf (op1, "#8");
605 	  sprintf (comm1, "r2 As==11");
606 	}
607       else if (regs == 3)
608 	{
609 	  *cycles = 1;
610 	  sprintf (op1, "#-1");
611 	  sprintf (comm1, "r3 As==11");
612 	}
613       else if (regs == 0)
614 	{
615 	  /* Absolute. @pc+  */
616 	  *cycles = 3;
617 	  dst = msp430dis_opcode (addr + 2, info);
618 	  cmd_len += 2;
619 	  sprintf (op1, "#0x%04x", PS (dst));
620 	}
621       else
622 	{
623 	  *cycles = 2;
624 	  sprintf (op1, "@r%d+", regs);
625 	}
626     }
627   else if (as == 1)
628     {
629       * cycles = 3;
630 
631       if (regs == 0)
632 	{
633 	  /* PC relative.  */
634 	  dst = msp430dis_opcode (addr + 2, info);
635 	  cmd_len += 2;
636 	  (*cycles)++;
637 	  sprintf (op1, "0x%04x", PS (dst));
638 	  sprintf (comm1, "PC rel. 0x%04x",
639 		   PS ((short) addr + 2 + dst));
640 	}
641       else if (regs == 2)
642 	{
643 	  /* Absolute.  */
644 	  dst = msp430dis_opcode (addr + 2, info);
645 	  cmd_len += 2;
646 	  sprintf (op1, "&0x%04x", PS (dst));
647 	}
648       else if (regs == 3)
649 	{
650 	  (*cycles)--;
651 	  sprintf (op1, "#1");
652 	  sprintf (comm1, "r3 As==01");
653 	}
654       else
655 	{
656 	  /* Indexd.  */
657 	  dst = msp430dis_opcode (addr + 2, info);
658 	  cmd_len += 2;
659 	  sprintf (op1, "%d(r%d)", dst, regs);
660 	}
661     }
662 
663   return cmd_len;
664 }
665 
666 int
print_insn_msp430(bfd_vma addr,disassemble_info * info)667 print_insn_msp430 (bfd_vma addr, disassemble_info *info)
668 {
669   void *stream = info->stream;
670   fprintf_ftype prin = info->fprintf_func;
671   struct msp430_opcode_s *opcode;
672   char op1[32], op2[32], comm1[64], comm2[64];
673   int cmd_len = 0;
674   unsigned short insn;
675   int cycles = 0;
676   char *bc = "";
677   char dinfo[32];		/* Debug purposes.  */
678 
679   insn = msp430dis_opcode (addr, info);
680   sprintf (dinfo, "0x%04x", insn);
681 
682   if (((int) addr & 0xffff) > 0xffdf)
683     {
684       (*prin) (stream, "interrupt service routine at 0x%04x", 0xffff & insn);
685       return 2;
686     }
687 
688   *comm1 = 0;
689   *comm2 = 0;
690 
691   for (opcode = msp430_opcodes; opcode->name; opcode++)
692     {
693       if ((insn & opcode->bin_mask) == opcode->bin_opcode
694 	  && opcode->bin_opcode != 0x9300)
695 	{
696 	  *op1 = 0;
697 	  *op2 = 0;
698 	  *comm1 = 0;
699 	  *comm2 = 0;
700 
701 	  /* r0 as destination. Ad should be zero.  */
702 	  if (opcode->insn_opnumb == 3 && (insn & 0x000f) == 0
703 	      && (0x0080 & insn) == 0)
704 	    {
705 	      cmd_len =
706 		msp430_branchinstr (info, opcode, addr, insn, op1, comm1,
707 				    &cycles);
708 	      if (cmd_len)
709 		break;
710 	    }
711 
712 	  switch (opcode->insn_opnumb)
713 	    {
714 	    case 0:
715 	      cmd_len = msp430_nooperands (opcode, addr, insn, comm1, &cycles);
716 	      break;
717 	    case 2:
718 	      cmd_len =
719 		msp430_doubleoperand (info, opcode, addr, insn, op1, op2,
720 				      comm1, comm2, &cycles);
721 	      if (insn & BYTE_OPERATION)
722 		bc = ".b";
723 	      break;
724 	    case 1:
725 	      cmd_len =
726 		msp430_singleoperand (info, opcode, addr, insn, op1, comm1,
727 				      &cycles);
728 	      if (insn & BYTE_OPERATION && opcode->fmt != 3)
729 		bc = ".b";
730 	      break;
731 	    default:
732 	      break;
733 	    }
734 	}
735 
736       if (cmd_len)
737 	break;
738     }
739 
740   dinfo[5] = 0;
741 
742   if (cmd_len < 1)
743     {
744       /* Unknown opcode, or invalid combination of operands.  */
745       (*prin) (stream, ".word	0x%04x;	????", PS (insn));
746       return 2;
747     }
748 
749   (*prin) (stream, "%s%s", opcode->name, bc);
750 
751   if (*op1)
752     (*prin) (stream, "\t%s", op1);
753   if (*op2)
754     (*prin) (stream, ",");
755 
756   if (strlen (op1) < 7)
757     (*prin) (stream, "\t");
758   if (!strlen (op1))
759     (*prin) (stream, "\t");
760 
761   if (*op2)
762     (*prin) (stream, "%s", op2);
763   if (strlen (op2) < 8)
764     (*prin) (stream, "\t");
765 
766   if (*comm1 || *comm2)
767     (*prin) (stream, ";");
768   else if (cycles)
769     {
770       if (*op2)
771 	(*prin) (stream, ";");
772       else
773 	{
774 	  if (strlen (op1) < 7)
775 	    (*prin) (stream, ";");
776 	  else
777 	    (*prin) (stream, "\t;");
778 	}
779     }
780   if (*comm1)
781     (*prin) (stream, "%s", comm1);
782   if (*comm1 && *comm2)
783     (*prin) (stream, ",");
784   if (*comm2)
785     (*prin) (stream, " %s", comm2);
786   return cmd_len;
787 }
788