xref: /csrg-svn/old/dbx/vax.c (revision 14338)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)vax.c 1.9 08/05/83";
4 
5 /*
6  * Target machine dependent stuff.
7  */
8 
9 #include "defs.h"
10 #include "machine.h"
11 #include "process.h"
12 #include "events.h"
13 #include "main.h"
14 #include "symbols.h"
15 #include "source.h"
16 #include "mappings.h"
17 #include "object.h"
18 #include "ops.h"
19 #include <signal.h>
20 
21 #ifndef public
22 typedef unsigned int Address;
23 typedef unsigned char Byte;
24 typedef unsigned int Word;
25 
26 #define NREG 16
27 
28 #define ARGP 12
29 #define FRP 13
30 #define STKP 14
31 #define PROGCTR 15
32 
33 #define BITSPERBYTE 8
34 #define BITSPERWORD (BITSPERBYTE * sizeof(Word))
35 
36 #define nargspassed(frame) argn(0, frame)
37 
38 #include "source.h"
39 #include "symbols.h"
40 
41 Address pc;
42 Address prtaddr;
43 
44 #endif
45 
46 private Address printop();
47 
48 /*
49  * Decode and print the instructions within the given address range.
50  */
51 
52 public printinst(lowaddr, highaddr)
53 Address lowaddr;
54 Address highaddr;
55 {
56     register Address addr;
57 
58     for (addr = lowaddr; addr <= highaddr; ) {
59 	addr = printop(addr);
60     }
61     prtaddr = addr;
62 }
63 
64 /*
65  * Another approach:  print n instructions starting at the given address.
66  */
67 
68 public printninst(count, addr)
69 int count;
70 Address addr;
71 {
72     register Integer i;
73     register Address newaddr;
74 
75     if (count <= 0) {
76 	error("non-positive repetition count");
77     } else {
78 	newaddr = addr;
79 	for (i = 0; i < count; i++) {
80 	    newaddr = printop(newaddr);
81 	}
82 	prtaddr = newaddr;
83     }
84 }
85 
86 /*
87  * Hacked version of adb's VAX instruction decoder.
88  */
89 
90 private Address printop(addr)
91 Address addr;
92 {
93     Optab op;
94     VaxOpcode ins;
95     unsigned char mode;
96     int argtype, amode, argno, argval;
97     String reg;
98     Boolean indexf;
99     short offset;
100 
101     argval = 0;
102     indexf = false;
103     printf("%08x  ", addr);
104     iread(&ins, addr, sizeof(ins));
105     addr += 1;
106     op = optab[ins];
107     printf("%s", op.iname);
108     for (argno = 0; argno < op.numargs; argno++) {
109 	if (indexf == true) {
110 	    indexf = false;
111 	} else if (argno == 0) {
112 	    printf("\t");
113 	} else {
114 	    printf(",");
115 	}
116 	argtype = op.argtype[argno];
117 	if (is_branch_disp(argtype)) {
118 	    mode = 0xAF + (typelen(argtype) << 5);
119 	} else {
120 	    iread(&mode, addr, sizeof(mode));
121 	    addr += 1;
122 	}
123 	reg = regname[regnm(mode)];
124 	amode = addrmode(mode);
125 	switch (amode) {
126 	    case LITSHORT:
127 	    case LITUPTO31:
128 	    case LITUPTO47:
129 	    case LITUPTO63:
130 		if (typelen(argtype) == TYPF || typelen(argtype) ==TYPD)
131 		    printf("$%s", fltimm[mode]);
132 		else
133 		    printf("$%x", mode);
134 		argval = mode;
135 		break;
136 
137 	    case INDEX:
138 		printf("[%s]", reg);
139 		indexf = true;
140 		argno--;
141 		break;
142 
143 	    case REG:
144 		printf("%s", reg);
145 		break;
146 
147 	    case REGDEF:
148 		printf("(%s)", reg);
149 		break;
150 
151 	    case AUTODEC:
152 		printf("-(%s)", reg);
153 		break;
154 
155 	    case AUTOINC:
156 		if (reg != regname[PROGCTR]) {
157 		    printf("(%s)+", reg);
158 		} else {
159 		    printf("$");
160 		    switch (typelen(argtype)) {
161 			case TYPB:
162 			    argval = printdisp(addr, 1, reg, amode);
163 			    addr += 1;
164 			    break;
165 
166 			case TYPW:
167 			    argval = printdisp(addr, 2, reg, amode);
168 			    addr += 2;
169 			    break;
170 
171 			case TYPL:
172 			    argval = printdisp(addr, 4, reg, amode);
173 			    addr += 4;
174 			    break;
175 
176 			case TYPF:
177 			    iread(&argval, addr, sizeof(argval));
178 			    printf("%06x", argval);
179 			    addr += 4;
180 			    break;
181 
182 			case TYPQ:
183 			case TYPD:
184 			    iread(&argval, addr, sizeof(argval));
185 			    printf("%06x", argval);
186 			    iread(&argval, addr+4, sizeof(argval));
187 			    printf("%06x", argval);
188 			    addr += 8;
189 			    break;
190 		    }
191 		}
192 		break;
193 
194 	    case AUTOINCDEF:
195 		if (reg == regname[PROGCTR]) {
196 		    printf("*$");
197 		    argval = printdisp(addr, 4, reg, amode);
198 		    addr += 4;
199 		} else {
200 		    printf("*(%s)+", reg);
201 		}
202 		break;
203 
204 	    case BYTEDISP:
205 		argval = printdisp(addr, 1, reg, amode);
206 		addr += 1;
207 		break;
208 
209 	    case BYTEDISPDEF:
210 		printf("*");
211 		argval = printdisp(addr, 1, reg, amode);
212 		addr += 1;
213 		break;
214 
215 	    case WORDDISP:
216 		argval = printdisp(addr, 2, reg, amode);
217 		addr += 2;
218 		break;
219 
220 	    case WORDDISPDEF:
221 		printf("*");
222 		argval = printdisp(addr, 2, reg, amode);
223 		addr += 2;
224 		break;
225 
226 	    case LONGDISP:
227 		argval = printdisp(addr, 4, reg, amode);
228 		addr += 4;
229 		break;
230 
231 	    case LONGDISPDEF:
232 		printf("*");
233 		argval = printdisp(addr, 4, reg, amode);
234 		addr += 4;
235 		break;
236 	}
237     }
238     if (ins == O_CASEB || ins == O_CASEW || ins == O_CASEL) {
239 	for (argno = 0; argno <= argval; argno++) {
240 	    iread(&offset, addr, sizeof(offset));
241 	    printf("\n\t\t%d", offset);
242 	    addr += 2;
243 	}
244     }
245     printf("\n");
246     return addr;
247 }
248 
249 /*
250  * Print the displacement of an instruction that uses displacement
251  * addressing.
252  */
253 
254 private int printdisp(addr, nbytes, reg, mode)
255 Address addr;
256 int nbytes;
257 char *reg;
258 int mode;
259 {
260     char byte;
261     short hword;
262     int argval;
263     Symbol f;
264 
265     switch (nbytes) {
266 	case 1:
267 	    iread(&byte, addr, sizeof(byte));
268 	    argval = byte;
269 	    break;
270 
271 	case 2:
272 	    iread(&hword, addr, sizeof(hword));
273 	    argval = hword;
274 	    break;
275 
276 	case 4:
277 	    iread(&argval, addr, sizeof(argval));
278 	    break;
279     }
280     if (reg == regname[PROGCTR] && mode >= BYTEDISP) {
281 	argval += addr + nbytes;
282     }
283     if (reg == regname[PROGCTR]) {
284 	f = whatblock((Address) argval + 2);
285 	if (codeloc(f) == argval + 2) {
286 	    printf("%s", symname(f));
287 	} else {
288 	    printf("%x", argval);
289 	}
290     } else {
291 	printf("%d(%s)", argval, reg);
292     }
293     return argval;
294 }
295 
296 /*
297  * Print the contents of the addresses within the given range
298  * according to the given format.
299  */
300 
301 typedef struct {
302     String name;
303     String printfstring;
304     int length;
305 } Format;
306 
307 private Format fmt[] = {
308     { "d", " %d", sizeof(short) },
309     { "D", " %ld", sizeof(long) },
310     { "o", " %o", sizeof(short) },
311     { "O", " %lo", sizeof(long) },
312     { "x", " %04x", sizeof(short) },
313     { "X", " %08x", sizeof(long) },
314     { "b", " \\%o", sizeof(char) },
315     { "c", " '%c'", sizeof(char) },
316     { "s", "%c", sizeof(char) },
317     { "f", " %f", sizeof(float) },
318     { "g", " %g", sizeof(double) },
319     { nil, nil, 0 }
320 };
321 
322 private Format *findformat(s)
323 String s;
324 {
325     register Format *f;
326 
327     f = &fmt[0];
328     while (f->name != nil and not streq(f->name, s)) {
329 	++f;
330     }
331     if (f->name == nil) {
332 	error("bad print format \"%s\"", s);
333     }
334     return f;
335 }
336 
337 public Address printdata(lowaddr, highaddr, format)
338 Address lowaddr;
339 Address highaddr;
340 String format;
341 {
342     register int n;
343     register Address addr;
344     register Format *f;
345     int value;
346 
347     if (lowaddr > highaddr) {
348 	error("first address larger than second");
349     }
350     f = findformat(format);
351     n = 0;
352     value = 0;
353     for (addr = lowaddr; addr <= highaddr; addr += f->length) {
354 	if (n == 0) {
355 	    printf("%08x: ", addr);
356 	}
357 	dread(&value, addr, f->length);
358 	printf(f->printfstring, value);
359 	++n;
360 	if (n >= (16 div f->length)) {
361 	    putchar('\n');
362 	    n = 0;
363 	}
364     }
365     if (n != 0) {
366 	putchar('\n');
367     }
368     prtaddr = addr;
369     return addr;
370 }
371 
372 /*
373  * The other approach is to print n items starting with a given address.
374  */
375 
376 public printndata(count, startaddr, format)
377 int count;
378 Address startaddr;
379 String format;
380 {
381     register int i, n;
382     register Address addr;
383     register Format *f;
384     register Boolean isstring;
385     char c;
386     union {
387 	char charv;
388 	short shortv;
389 	int intv;
390 	float floatv;
391 	double doublev;
392     } value;
393 
394     if (count <= 0) {
395 	error("non-positive repetition count");
396     }
397     f = findformat(format);
398     isstring = (Boolean) streq(f->name, "s");
399     n = 0;
400     addr = startaddr;
401     value.intv = 0;
402     for (i = 0; i < count; i++) {
403 	if (n == 0) {
404 	    printf("%08x: ", addr);
405 	}
406 	if (isstring) {
407 	    putchar('"');
408 	    dread(&c, addr, sizeof(char));
409 	    while (c != '\0') {
410 		printchar(c);
411 		++addr;
412 		dread(&c, addr, sizeof(char));
413 	    }
414 	    putchar('"');
415 	    putchar('\n');
416 	    n = 0;
417 	    addr += sizeof(String);
418 	} else {
419 	    dread(&value, addr, f->length);
420 	    printf(f->printfstring, value);
421 	    ++n;
422 	    if (n >= (16 div f->length)) {
423 		putchar('\n');
424 		n = 0;
425 	    }
426 	    addr += f->length;
427 	}
428     }
429     if (n != 0) {
430 	putchar('\n');
431     }
432     prtaddr = addr;
433 }
434 
435 /*
436  * Print out a value according to the given format.
437  */
438 
439 public printvalue(v, format)
440 long v;
441 String format;
442 {
443     Format *f;
444     char *p, *q;
445 
446     f = findformat(format);
447     if (streq(f->name, "s")) {
448 	putchar('"');
449 	p = (char *) &v;
450 	q = p + sizeof(v);
451 	while (p < q) {
452 	    printchar(*p);
453 	    ++p;
454 	}
455 	putchar('"');
456     } else {
457 	printf(f->printfstring, v);
458     }
459     putchar('\n');
460 }
461 
462 /*
463  * Print out an execution time error.
464  * Assumes the source position of the error has been calculated.
465  *
466  * Have to check if the -r option was specified; if so then
467  * the object file information hasn't been read in yet.
468  */
469 
470 public printerror()
471 {
472     extern Integer sys_nsig;
473     extern String sys_siglist[];
474     Integer err;
475 
476     if (isfinished(process)) {
477 	printf("\"%s\" exits with code %d\n", objname, exitcode(process));
478 	erecover();
479     }
480     if (runfirst) {
481 	fprintf(stderr, "Entering debugger ...");
482 	init();
483 	fprintf(stderr, " type 'help' for help\n");
484     }
485     err = errnum(process);
486     if (err == SIGINT) {
487 	printf("\n\ninterrupt ");
488 	printloc();
489     } else if (err == SIGTRAP) {
490 	printf("\nerror ");
491 	printloc();
492     } else {
493 	if (err < 0 or err > sys_nsig) {
494 	    printf("\nsignal %d ", err);
495 	} else {
496 	    printf("\n%s ", sys_siglist[err]);
497 	}
498 	printloc();
499     }
500     putchar('\n');
501     if (curline > 0) {
502 	printlines(curline, curline);
503     } else {
504 	printinst(pc, pc);
505     }
506     erecover();
507 }
508 
509 /*
510  * Note the termination of the program.  We do this so as to avoid
511  * having the process exit, which would make the values of variables
512  * inaccessible.  We do want to flush all output buffers here,
513  * otherwise it'll never get done.
514  */
515 
516 public endprogram()
517 {
518     Integer exitcode;
519 
520     stepto(nextaddr(pc, true));
521     printnews();
522     exitcode = argn(1, nil);
523     printf("\nexecution completed, exit code is %d\n", exitcode);
524     getsrcpos();
525     erecover();
526 }
527 
528 /*
529  * Single step the machine a source line (or instruction if "inst_tracing"
530  * is true).  If "isnext" is true, skip over procedure calls.
531  */
532 
533 private Address getcall();
534 
535 public dostep(isnext)
536 Boolean isnext;
537 {
538     register Address addr;
539     register Lineno line;
540     String filename;
541 
542     addr = nextaddr(pc, isnext);
543     if (not inst_tracing and nlhdr.nlines != 0) {
544 	line = linelookup(addr);
545 	while (line == 0) {
546 	    addr = nextaddr(addr, isnext);
547 	    line = linelookup(addr);
548 	}
549 	curline = line;
550     } else {
551 	curline = 0;
552     }
553     stepto(addr);
554     filename = srcfilename(addr);
555     setsource(filename);
556 }
557 
558 /*
559  * Compute the next address that will be executed from the given one.
560  * If "isnext" is true then consider a procedure call as straight line code.
561  *
562  * We must unfortunately do much of the same work that is necessary
563  * to print instructions.  In addition we have to deal with branches.
564  * Unconditional branches we just follow, for conditional branches
565  * we continue execution to the current location and then single step
566  * the machine.  We assume that the last argument in an instruction
567  * that branches is the branch address (or relative offset).
568  */
569 
570 public Address nextaddr(startaddr, isnext)
571 Address startaddr;
572 Boolean isnext;
573 {
574     register Address addr;
575     Optab op;
576     VaxOpcode ins;
577     unsigned char mode;
578     int argtype, amode, argno, argval;
579     String r;
580     Boolean indexf;
581     enum { KNOWN, SEQUENTIAL, BRANCH } addrstatus;
582 
583     argval = 0;
584     indexf = false;
585     addr = startaddr;
586     iread(&ins, addr, sizeof(ins));
587     switch (ins) {
588 	case O_BRB:
589 	case O_BRW:
590 	case O_JMP:
591 	    addrstatus = BRANCH;
592 	    break;
593 
594 	case O_BSBB:
595 	case O_BSBW:
596 	case O_JSB:
597 	case O_CALLG:
598 	case O_CALLS:
599 	    if (isnext) {
600 		addrstatus = SEQUENTIAL;
601 	    } else {
602 		addrstatus = KNOWN;
603 		stepto(addr);
604 		pstep(process);
605 		addr = reg(PROGCTR);
606 		pc = addr;
607 		curfunc = whatblock(pc);
608 		if (not isbperr()) {
609 		    printstatus();
610 		    /* NOTREACHED */
611 		}
612 		bpact();
613 		if (nosource(curfunc) and canskip(curfunc) and
614 		  nlhdr.nlines != 0) {
615 		    addrstatus = KNOWN;
616 		    addr = return_addr();
617 		    stepto(addr);
618 		    bpact();
619 		} else {
620 		    callnews(/* iscall = */ true);
621 		}
622 	    }
623 	    break;
624 
625 	case O_RSB:
626 	case O_RET:
627 	    addrstatus = KNOWN;
628 	    callnews(/* iscall = */ false);
629 	    addr = return_addr();
630 	    stepto(addr);
631 	    bpact();
632 	    break;
633 
634 	case O_BNEQ: case O_BEQL: case O_BGTR:
635 	case O_BLEQ: case O_BGEQ: case O_BLSS:
636 	case O_BGTRU: case O_BLEQU: case O_BVC:
637 	case O_BVS: case O_BCC: case O_BCS:
638 	case O_CASEB: case O_CASEW: case O_CASEL:
639 	case O_BBS: case O_BBC: case O_BBSS: case O_BBCS:
640 	case O_BBSC: case O_BBCC: case O_BBSSI:
641 	case O_BBCCI: case O_BLBS: case O_BLBC:
642 	case O_ACBL: case O_AOBLSS: case O_AOBLEQ:
643 	case O_SOBGEQ: case O_SOBGTR:
644 	    addrstatus = KNOWN;
645 	    stepto(addr);
646 	    pstep(process);
647 	    addr = reg(PROGCTR);
648 	    pc = addr;
649 	    if (not isbperr()) {
650 		printstatus();
651 	    }
652 	    break;
653 
654 	default:
655 	    addrstatus = SEQUENTIAL;
656 	    break;
657     }
658     if (addrstatus != KNOWN) {
659 	addr += 1;
660 	op = optab[ins];
661 	for (argno = 0; argno < op.numargs; argno++) {
662 	    if (indexf == true) {
663 		indexf = false;
664 	    }
665 	    argtype = op.argtype[argno];
666 	    if (is_branch_disp(argtype)) {
667 		mode = 0xAF + (typelen(argtype) << 5);
668 	    } else {
669 		iread(&mode, addr, sizeof(mode));
670 		addr += 1;
671 	    }
672 	    r = regname[regnm(mode)];
673 	    amode = addrmode(mode);
674 	    switch (amode) {
675 		case LITSHORT:
676 		case LITUPTO31:
677 		case LITUPTO47:
678 		case LITUPTO63:
679 		    argval = mode;
680 		    break;
681 
682 		case INDEX:
683 		    indexf = true;
684 		    --argno;
685 		    break;
686 
687 		case REG:
688 		case REGDEF:
689 		case AUTODEC:
690 		    break;
691 
692 		case AUTOINC:
693 		    if (r == regname[PROGCTR]) {
694 			switch (typelen(argtype)) {
695 			    case TYPB:
696 				argval = getdisp(addr, 1, r, amode);
697 				addr += 1;
698 				break;
699 
700 			    case TYPW:
701 				argval = getdisp(addr, 2, r, amode);
702 				addr += 2;
703 				break;
704 
705 			    case TYPL:
706 				argval = getdisp(addr, 4, r, amode);
707 				addr += 4;
708 				break;
709 
710 			    case TYPF:
711 				iread(&argval, addr, sizeof(argval));
712 				addr += 4;
713 				break;
714 
715 			    case TYPQ:
716 			    case TYPD:
717 				iread(&argval, addr+4, sizeof(argval));
718 				addr += 8;
719 				break;
720 			}
721 		    }
722 		    break;
723 
724 		case AUTOINCDEF:
725 		    if (r == regname[PROGCTR]) {
726 			argval = getdisp(addr, 4, r, amode);
727 			addr += 4;
728 		    }
729 		    break;
730 
731 		case BYTEDISP:
732 		case BYTEDISPDEF:
733 		    argval = getdisp(addr, 1, r, amode);
734 		    addr += 1;
735 		    break;
736 
737 		case WORDDISP:
738 		case WORDDISPDEF:
739 		    argval = getdisp(addr, 2, r, amode);
740 		    addr += 2;
741 		    break;
742 
743 		case LONGDISP:
744 		case LONGDISPDEF:
745 		    argval = getdisp(addr, 4, r, amode);
746 		    addr += 4;
747 		    break;
748 	    }
749 	}
750 	if (ins == O_CALLS or ins == O_CALLG) {
751 	    argval += 2;
752 	}
753 	if (addrstatus == BRANCH) {
754 	    addr = argval;
755 	}
756     }
757     return addr;
758 }
759 
760 /*
761  * Get the displacement of an instruction that uses displacement addressing.
762  */
763 
764 private int getdisp(addr, nbytes, reg, mode)
765 Address addr;
766 int nbytes;
767 String reg;
768 int mode;
769 {
770     char byte;
771     short hword;
772     int argval;
773 
774     switch (nbytes) {
775 	case 1:
776 	    iread(&byte, addr, sizeof(byte));
777 	    argval = byte;
778 	    break;
779 
780 	case 2:
781 	    iread(&hword, addr, sizeof(hword));
782 	    argval = hword;
783 	    break;
784 
785 	case 4:
786 	    iread(&argval, addr, sizeof(argval));
787 	    break;
788     }
789     if (reg == regname[PROGCTR] && mode >= BYTEDISP) {
790 	argval += addr + nbytes;
791     }
792     return argval;
793 }
794 
795 #define BP_OP       O_BPT       /* breakpoint trap */
796 #define BP_ERRNO    SIGTRAP     /* signal received at a breakpoint */
797 
798 /*
799  * Setting a breakpoint at a location consists of saving
800  * the word at the location and poking a BP_OP there.
801  *
802  * We save the locations and words on a list for use in unsetting.
803  */
804 
805 typedef struct Savelist *Savelist;
806 
807 struct Savelist {
808     Address location;
809     Byte save;
810     Byte refcount;
811     Savelist link;
812 };
813 
814 private Savelist savelist;
815 
816 /*
817  * Set a breakpoint at the given address.  Only save the word there
818  * if it's not already a breakpoint.
819  */
820 
821 public setbp(addr)
822 Address addr;
823 {
824     Byte w;
825     Byte save;
826     register Savelist newsave, s;
827 
828     for (s = savelist; s != nil; s = s->link) {
829 	if (s->location == addr) {
830 	    s->refcount++;
831 	    return;
832 	}
833     }
834     iread(&save, addr, sizeof(save));
835     newsave = new(Savelist);
836     newsave->location = addr;
837     newsave->save = save;
838     newsave->refcount = 1;
839     newsave->link = savelist;
840     savelist = newsave;
841     w = BP_OP;
842     iwrite(&w, addr, sizeof(w));
843 }
844 
845 /*
846  * Unset a breakpoint; unfortunately we have to search the SAVELIST
847  * to find the saved value.  The assumption is that the SAVELIST will
848  * usually be quite small.
849  */
850 
851 public unsetbp(addr)
852 Address addr;
853 {
854     register Savelist s, prev;
855 
856     prev = nil;
857     for (s = savelist; s != nil; s = s->link) {
858 	if (s->location == addr) {
859 	    iwrite(&s->save, addr, sizeof(s->save));
860 	    s->refcount--;
861 	    if (s->refcount == 0) {
862 		if (prev == nil) {
863 		    savelist = s->link;
864 		} else {
865 		    prev->link = s->link;
866 		}
867 		dispose(s);
868 	    }
869 	    return;
870 	}
871 	prev = s;
872     }
873     panic("unsetbp: couldn't find address %d", addr);
874 }
875 
876 /*
877  * Predicate to test if the reason the process stopped was because
878  * of a breakpoint.
879  */
880 
881 public Boolean isbperr()
882 {
883     return (Boolean) (not isfinished(process) and errnum(process) == SIGTRAP);
884 }
885 
886 /*
887  * Enter a procedure by creating and executing a call instruction.
888  */
889 
890 #define CALLSIZE 7	/* size of call instruction */
891 
892 public beginproc(p, argc)
893 Symbol p;
894 Integer argc;
895 {
896     char save[CALLSIZE];
897     struct {
898 	VaxOpcode op;
899 	unsigned char numargs;
900 	unsigned char mode;
901 	char addr[sizeof(long)];	/* unaligned long */
902     } call;
903     long dest;
904 
905     pc = 2;
906     iread(save, pc, sizeof(save));
907     call.op = O_CALLS;
908     call.numargs = argc;
909     call.mode = 0xef;
910     dest = codeloc(p) - 2 - (pc + 7);
911     mov(&dest, call.addr, sizeof(call.addr));
912     iwrite(&call, pc, sizeof(call));
913     setreg(PROGCTR, pc);
914     pstep(process);
915     iwrite(save, pc, sizeof(save));
916     pc = reg(PROGCTR);
917     if (not isbperr()) {
918 	printstatus();
919     }
920 }
921