1 /*	$Id: code.c,v 1.85 2014/09/15 14:06:26 ragge Exp $	*/
2 /*
3  * Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 # include "pass1.h"
31 
32 /*
33  * Print out assembler segment name.
34  */
35 void
setseg(int seg,char * name)36 setseg(int seg, char *name)
37 {
38 	switch (seg) {
39 	case PROG: name = ".text"; break;
40 	case DATA:
41 	case LDATA: name = ".data"; break;
42 	case UDATA: break;
43 #ifdef MACHOABI
44 	case PICLDATA:
45 	case PICDATA: name = ".section .data.rel.rw,\"aw\""; break;
46 	case PICRDATA: name = ".section .data.rel.ro,\"aw\""; break;
47 	case STRNG: name = ".cstring"; break;
48 	case RDATA: name = ".const_data"; break;
49 #else
50 	case PICLDATA: name = ".section .data.rel.local,\"aw\",@progbits";break;
51 	case PICDATA: name = ".section .data.rel.rw,\"aw\",@progbits"; break;
52 	case PICRDATA: name = ".section .data.rel.ro,\"aw\",@progbits"; break;
53 	case STRNG:
54 #ifdef AOUTABI
55 	case RDATA: name = ".data"; break;
56 #else
57 	case RDATA: name = ".section .rodata"; break;
58 #endif
59 #endif
60 	case TLSDATA: name = ".section .tdata,\"awT\",@progbits"; break;
61 	case TLSUDATA: name = ".section .tbss,\"awT\",@nobits"; break;
62 #ifdef MACHOABI
63 	case CTORS: name = ".mod_init_func\n\t.align 2"; break;
64 	case DTORS: name = ".mod_term_func\n\t.align 2"; break;
65 #else
66 	case CTORS: name = ".section\t.ctors,\"aw\",@progbits"; break;
67 	case DTORS: name = ".section\t.dtors,\"aw\",@progbits"; break;
68 #endif
69 	case NMSEG:
70 		printf("\t.section %s,\"a%c\",@progbits\n", name,
71 		    cftnsp ? 'x' : 'w');
72 		return;
73 	}
74 	printf("\t%s\n", name);
75 }
76 
77 #ifdef MACHOABI
78 void
defalign(int al)79 defalign(int al)
80 {
81 	printf("\t.align %d\n", ispow2(al/ALCHAR));
82 }
83 #endif
84 
85 /*
86  * Define everything needed to print out some data (or text).
87  * This means segment, alignment, visibility, etc.
88  */
89 void
defloc(struct symtab * sp)90 defloc(struct symtab *sp)
91 {
92 	char *name;
93 
94 	if ((name = sp->soname) == NULL)
95 		name = exname(sp->sname);
96 	if (sp->sclass == EXTDEF) {
97 		printf("	.globl %s\n", name);
98 #if defined(ELFABI)
99 		printf("\t.type %s,@%s\n", name,
100 		    ISFTN(sp->stype)? "function" : "object");
101 #endif
102 	}
103 #if defined(ELFABI)
104 	if (!ISFTN(sp->stype)) {
105 		if (sp->slevel == 0)
106 			printf("\t.size %s,%d\n", name,
107 			    (int)tsize(sp->stype, sp->sdf, sp->sap)/SZCHAR);
108 		else
109 			printf("\t.size " LABFMT ",%d\n", sp->soffset,
110 			    (int)tsize(sp->stype, sp->sdf, sp->sap)/SZCHAR);
111 	}
112 #endif
113 	if (sp->slevel == 0)
114 		printf("%s:\n", name);
115 	else
116 		printf(LABFMT ":\n", sp->soffset);
117 }
118 
119 int structrettemp;
120 
121 /*
122  * code for the end of a function
123  * deals with struct return here
124  */
125 void
efcode(void)126 efcode(void)
127 {
128 	extern int gotnr;
129 	NODE *p, *q;
130 
131 	gotnr = 0;	/* new number for next fun */
132 	if (cftnsp->stype != STRTY+FTN && cftnsp->stype != UNIONTY+FTN)
133 		return;
134 
135 	/* struct return for small structs */
136 	int sz = tsize(BTYPE(cftnsp->stype), cftnsp->sdf, cftnsp->sap);
137 #if defined(os_openbsd)
138 	if (sz == SZCHAR || sz == SZSHORT || sz == SZINT || sz == SZLONGLONG) {
139 #else
140 	if (sz == SZLONGLONG && attr_find(cftnsp->sap, ATTR_COMPLEX)) {
141 #endif
142 		/* Pointer to struct in eax */
143 		if (sz == SZLONGLONG) {
144 			q = block(OREG, NIL, NIL, INT, 0, 0);
145 			q->n_lval = 4;
146 			p = block(REG, NIL, NIL, INT, 0, 0);
147 			p->n_rval = EDX;
148 			ecomp(buildtree(ASSIGN, p, q));
149 		}
150 		if (sz < SZSHORT) sz = CHAR;
151 		else if (sz > SZSHORT) sz = INT;
152 		else sz = SHORT;
153 		q = block(OREG, NIL, NIL, sz, 0, 0);
154 		p = block(REG, NIL, NIL, sz, 0, 0);
155 		ecomp(buildtree(ASSIGN, p, q));
156 		return;
157 	}
158 
159 	/* Create struct assignment */
160 	q = tempnode(structrettemp, PTR+STRTY, 0, cftnsp->sap);
161 	q = buildtree(UMUL, q, NIL);
162 	p = block(REG, NIL, NIL, PTR+STRTY, 0, cftnsp->sap);
163 	p = buildtree(UMUL, p, NIL);
164 	p = buildtree(ASSIGN, q, p);
165 	ecomp(p);
166 
167 	/* put hidden arg in eax on return */
168 	q = tempnode(structrettemp, INT, 0, 0);
169 	p = block(REG, NIL, NIL, INT, 0, 0);
170 	regno(p) = EAX;
171 	ecomp(buildtree(ASSIGN, p, q));
172 }
173 
174 static TWORD longregs[] = { EAXEDX, EDXECX };
175 static TWORD regpregs[] = { EAX, EDX, ECX };
176 static TWORD charregs[] = { AL, DL, CL };
177 
178 /*
179  * code for the beginning of a function; a is an array of
180  * indices in symtab for the arguments; n is the number
181  *
182  * Classifying args on i386; not simple:
183  * - Args may be on stack or in registers (regparm)
184  * - There may be a hidden first arg, unless OpenBSD struct return.
185  * - Regparm syntax is not well documented.
186  * - There may be stdcall functions, where the called function pops stack
187  * - ...probably more
188  */
189 void
190 bfcode(struct symtab **sp, int cnt)
191 {
192 	extern int argstacksize;
193 #ifdef GCC_COMPAT
194 	struct attr *ap;
195 #endif
196 	struct symtab *sp2;
197 	extern int gotnr;
198 	NODE *n, *p;
199 	int i, regparmarg;
200 	int argbase, nrarg, sz;
201 
202 	/* Take care of PIC stuff first */
203         if (kflag) {
204 #define STL     200
205                 char *str = inlalloc(STL);
206 #if !defined(MACHOABI)
207                 int l = getlab();
208 #else
209                 char *name;
210 #endif
211 
212                 /* Generate extended assembler for PIC prolog */
213                 p = tempnode(0, INT, 0, 0);
214                 gotnr = regno(p);
215                 p = block(XARG, p, NIL, INT, 0, 0);
216                 p->n_name = "=g";
217                 p = block(XASM, p, bcon(0), INT, 0, 0);
218 
219 #if defined(MACHOABI)
220                 if ((name = cftnsp->soname) == NULL)
221                         name = cftnsp->sname;
222                 if (snprintf(str, STL, "call L%s$pb\nL%s$pb:\n\tpopl %%0\n",
223                     name, name) >= STL)
224                         cerror("bfcode");
225 #else
226                 if (snprintf(str, STL,
227                     "call " LABFMT "\n" LABFMT ":\n\tpopl %%0\n"
228                     "\taddl $_GLOBAL_OFFSET_TABLE_+[.-" LABFMT "], %%0\n",
229                     l, l, l) >= STL)
230                         cerror("bfcode");
231 #endif
232                 p->n_name = str;
233                 p->n_right->n_type = STRTY;
234                 ecomp(p);
235         }
236 
237 	argbase = ARGINIT;
238 	nrarg = regparmarg = 0;
239 	argstacksize = 0;
240 
241 #ifdef GCC_COMPAT
242         if (attr_find(cftnsp->sap, GCC_ATYP_STDCALL) != NULL)
243                 cftnsp->sflags |= SSTDCALL;
244         if ((ap = attr_find(cftnsp->sap, GCC_ATYP_REGPARM)))
245                 regparmarg = ap->iarg(0);
246 #endif
247 
248 	/* Function returns struct, create return arg node */
249 	if (cftnsp->stype == STRTY+FTN || cftnsp->stype == UNIONTY+FTN) {
250 		sz = tsize(BTYPE(cftnsp->stype), cftnsp->sdf, cftnsp->sap);
251 #if defined(os_openbsd)
252 		/* OpenBSD uses non-standard return for small structs */
253 		if (sz > SZLONGLONG)
254 #else
255 		if (sz != SZLONGLONG ||
256 		    attr_find(cftnsp->sap, ATTR_COMPLEX) == 0)
257 #endif
258 		{
259 			if (regparmarg) {
260 				n = block(REG, 0, 0, INT, 0, 0);
261 				regno(n) = regpregs[nrarg++];
262 			} else {
263 				n = block(OREG, 0, 0, INT, 0, 0);
264 				n->n_lval = argbase/SZCHAR;
265 				argbase += SZINT;
266 				regno(n) = FPREG;
267 				argstacksize += 4; /* popped by callee */
268 			}
269 			p = tempnode(0, INT, 0, 0);
270 			structrettemp = regno(p);
271 			p = buildtree(ASSIGN, p, n);
272 			ecomp(p);
273 		}
274 	}
275 
276 	/*
277 	 * Find where all params are so that they end up at the right place.
278 	 * At the same time recalculate their arg offset on stack.
279 	 * We also get the "pop size" for stdcall.
280 	 */
281 	for (i = 0; i < cnt; i++) {
282 		sp2 = sp[i];
283 		sz = tsize(sp2->stype, sp2->sdf, sp2->sap);
284 
285 		SETOFF(sz, SZINT);
286 
287 		if (cisreg(sp2->stype) == 0 ||
288 		    ((regparmarg - nrarg) * SZINT < sz)) {	/* not in reg */
289 			sp2->soffset = argbase;
290 			argbase += sz;
291 			nrarg = regparmarg;	/* no more in reg either */
292 		} else {					/* in reg */
293 			sp2->soffset = nrarg;
294 			nrarg += sz/SZINT;
295 			sp2->sclass = REGISTER;
296 		}
297 	}
298 
299 	/*
300 	 * Now (argbase - ARGINIT) is used space on stack.
301 	 * Move (if necessary) the args to something new.
302 	 */
303 	for (i = 0; i < cnt; i++) {
304 		int reg, j;
305 
306 		sp2 = sp[i];
307 
308 		if (ISSOU(sp2->stype) && sp2->sclass == REGISTER) {
309 			/* must move to stack */
310 			sz = tsize(sp2->stype, sp2->sdf, sp2->sap);
311 			SETOFF(sz, SZINT);
312 			SETOFF(autooff, SZINT);
313 			reg = sp2->soffset;
314 			sp2->sclass = AUTO;
315 			sp2->soffset = NOOFFSET;
316 			oalloc(sp2, &autooff);
317                         for (j = 0; j < sz/SZCHAR; j += 4) {
318                                 p = block(OREG, 0, 0, INT, 0, 0);
319                                 p->n_lval = sp2->soffset/SZCHAR + j;
320                                 regno(p) = FPREG;
321                                 n = block(REG, 0, 0, INT, 0, 0);
322                                 regno(n) = regpregs[reg++];
323                                 p = block(ASSIGN, p, n, INT, 0, 0);
324                                 ecomp(p);
325                         }
326 		} else if (cisreg(sp2->stype) && !ISSOU(sp2->stype) &&
327 		    ((cqual(sp2->stype, sp2->squal) & VOL) == 0) && xtemps) {
328 			/* just put rest in temps */
329 			if (sp2->sclass == REGISTER) {
330 				n = block(REG, 0, 0, sp2->stype,
331 				    sp2->sdf, sp2->sap);
332 				if (ISLONGLONG(sp2->stype))
333 					regno(n) = longregs[sp2->soffset];
334 				else if (DEUNSIGN(sp2->stype) == CHAR ||
335 				    sp2->stype == BOOL)
336 					regno(n) = charregs[sp2->soffset];
337 				else
338 					regno(n) = regpregs[sp2->soffset];
339 			} else {
340                                 n = block(OREG, 0, 0, sp2->stype,
341 				    sp2->sdf, sp2->sap);
342                                 n->n_lval = sp2->soffset/SZCHAR;
343                                 regno(n) = FPREG;
344 			}
345 			p = tempnode(0, sp2->stype, sp2->sdf, sp2->sap);
346 			sp2->soffset = regno(p);
347 			sp2->sflags |= STNODE;
348 			n = buildtree(ASSIGN, p, n);
349 			ecomp(n);
350 		}
351 	}
352 
353         if (cftnsp->sflags & SSTDCALL) {
354 		/* XXX interaction STDCALL and struct return? */
355 		argstacksize += (argbase - ARGINIT)/SZCHAR;
356 #ifdef os_win32
357 
358                 char buf[256];
359                 char *name;
360                 /*
361                  * mangle name in symbol table as a callee.
362                  */
363                 if ((name = cftnsp->soname) == NULL)
364                         name = exname(cftnsp->sname);
365                 snprintf(buf, 256, "%s@%d", name, argstacksize);
366                 cftnsp->soname = addname(buf);
367 #endif
368         }
369 
370 }
371 
372 #if defined(MACHOABI)
373 struct stub stublist;
374 struct stub nlplist;
375 #endif
376 
377 /* called just before final exit */
378 /* flag is 1 if errors, 0 if none */
379 void
380 ejobcode(int flag)
381 {
382 #if defined(MACHOABI)
383 	/*
384 	 * iterate over the stublist and output the PIC stubs
385 `	 */
386 	if (kflag) {
387 		struct stub *p;
388 
389 		DLIST_FOREACH(p, &stublist, link) {
390 			printf("\t.section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5\n");
391 			printf("L%s$stub:\n", p->name);
392 			printf("\t.indirect_symbol %s\n", p->name);
393 			printf("\thlt ; hlt ; hlt ; hlt ; hlt\n");
394 			printf("\t.subsections_via_symbols\n");
395 		}
396 
397 		printf("\t.section __IMPORT,__pointers,non_lazy_symbol_pointers\n");
398 		DLIST_FOREACH(p, &nlplist, link) {
399 			printf("L%s$non_lazy_ptr:\n", p->name);
400 			printf("\t.indirect_symbol %s\n", p->name);
401 			printf("\t.long 0\n");
402 	        }
403 
404 	}
405 #endif
406 
407 	printf("\t.ident \"PCC: %s\"\n", VERSSTR);
408 }
409 
410 void
411 bjobcode(void)
412 {
413 #ifdef os_sunos
414 	astypnames[SHORT] = astypnames[USHORT] = "\t.2byte";
415 #endif
416 	astypnames[INT] = astypnames[UNSIGNED] = "\t.long";
417 #if defined(MACHOABI)
418 	DLIST_INIT(&stublist, link);
419 	DLIST_INIT(&nlplist, link);
420 #endif
421 #if defined(__GNUC__) || defined(__PCC__)
422 	/* Be sure that the compiler uses full x87 */
423 	/* XXX cross-compiling will fail here */
424 	int fcw;
425 	__asm("fstcw (%0)" : : "r"(&fcw));
426 	fcw |= 0x300;
427 	__asm("fldcw (%0)" : : "r"(&fcw));
428 #endif
429 }
430 
431 /*
432  * Convert FUNARG to assign in case of regparm.
433  */
434 static int regcvt, rparg;
435 static void
436 addreg(NODE *p)
437 {
438 	TWORD t;
439 	NODE *q;
440 	int sz, r;
441 
442 	sz = tsize(p->n_type, p->n_df, p->n_ap)/SZCHAR;
443 	sz = (sz + 3) >> 2;	/* sz in regs */
444 	if ((regcvt+sz) > rparg) {
445 		regcvt = rparg;
446 		return;
447 	}
448 	if (sz > 2)
449 		uerror("cannot put struct in 3 regs (yet)");
450 
451 	if (sz == 2)
452 		r = regcvt == 0 ? EAXEDX : EDXECX;
453 	else
454 		r = regcvt == 0 ? EAX : regcvt == 1 ? EDX : ECX;
455 
456 	if (p->n_op == FUNARG) {
457 		/* at most 2 regs */
458 		if (p->n_type < INT) {
459 			p->n_left = ccast(p->n_left, INT, 0, 0, 0);
460 			p->n_type = INT;
461 		}
462 
463 		p->n_op = ASSIGN;
464 		p->n_right = p->n_left;
465 	} else if (p->n_op == STARG) {
466 		/* convert to ptr, put in reg */
467 		q = p->n_left;
468 		t = sz == 2 ? LONGLONG : INT;
469 		q = cast(q, INCREF(t), 0);
470 		q = buildtree(UMUL, q, NIL);
471 		p->n_op = ASSIGN;
472 		p->n_type = t;
473 		p->n_right = q;
474 	} else
475 		cerror("addreg");
476 	p->n_left = block(REG, 0, 0, p->n_type, 0, 0);
477 	regno(p->n_left) = r;
478 	regcvt += sz;
479 }
480 
481 /*
482  * Called with a function call with arguments as argument.
483  * This is done early in buildtree() and only done once.
484  * Returns p.
485  */
486 NODE *
487 funcode(NODE *p)
488 {
489 	extern int gotnr;
490 #ifdef GCC_COMPAT
491 	struct attr *ap;
492 #endif
493 	NODE *r, *l;
494 	TWORD t = DECREF(DECREF(p->n_left->n_type));
495 	int stcall;
496 
497 	stcall = ISSOU(t);
498 	/*
499 	 * We may have to prepend:
500 	 * - Hidden arg0 for struct return (in reg or on stack).
501 	 * - ebx in case of PIC code.
502 	 */
503 
504 	/* Fix function call arguments. On x86, just add funarg */
505 	for (r = p->n_right; r->n_op == CM; r = r->n_left) {
506 		if (r->n_right->n_op != STARG)
507 			r->n_right = block(FUNARG, r->n_right, NIL,
508 			    r->n_right->n_type, r->n_right->n_df,
509 			    r->n_right->n_ap);
510 	}
511 	if (r->n_op != STARG) {
512 		l = talloc();
513 		*l = *r;
514 		r->n_op = FUNARG;
515 		r->n_left = l;
516 		r->n_type = l->n_type;
517 	}
518 #ifdef os_openbsd
519 	if (stcall && (ap = strattr(p->n_left->n_ap)) &&
520 	    ap->amsize != SZCHAR && ap->amsize != SZSHORT &&
521 	    ap->amsize != SZINT && ap->amsize != SZLONGLONG)
522 #else
523 	if (stcall &&
524 	    (attr_find(p->n_left->n_ap, ATTR_COMPLEX) == 0 ||
525 	     ((ap = strattr(p->n_left->n_ap)) && ap->amsize > SZLONGLONG)))
526 #endif
527 	{
528 		/* Prepend a placeholder for struct address. */
529 		/* Use EBP, can never show up under normal circumstances */
530 		l = talloc();
531 		*l = *r;
532 		r->n_op = CM;
533 		r->n_right = l;
534 		r->n_type = INT;
535 		l = block(REG, 0, 0, INCREF(VOID), 0, 0);
536 		regno(l) = EBP;
537 		l = block(FUNARG, l, 0, INCREF(VOID), 0, 0);
538 		r->n_left = l;
539 	}
540 
541 #ifdef GCC_COMPAT
542 	if ((ap = attr_find(p->n_left->n_ap, GCC_ATYP_REGPARM)))
543 		rparg = ap->iarg(0);
544 	else
545 #endif
546 		rparg = 0;
547 
548 	regcvt = 0;
549 	if (rparg)
550 		listf(p->n_right, addreg);
551 
552 	if (kflag == 0)
553 		return p;
554 
555 #if defined(ELFABI)
556 	/* Create an ASSIGN node for ebx */
557 	l = block(REG, NIL, NIL, INT, 0, 0);
558 	l->n_rval = EBX;
559 	l = buildtree(ASSIGN, l, tempnode(gotnr, INT, 0, 0));
560 	if (p->n_right->n_op != CM) {
561 		p->n_right = block(CM, l, p->n_right, INT, 0, 0);
562 	} else {
563 		for (r = p->n_right; r->n_left->n_op == CM; r = r->n_left)
564 			;
565 		r->n_left = block(CM, l, r->n_left, INT, 0, 0);
566 	}
567 #endif
568 	return p;
569 }
570 
571 /* fix up type of field p */
572 void
573 fldty(struct symtab *p)
574 {
575 }
576 
577 /*
578  * XXX - fix genswitch.
579  */
580 int
581 mygenswitch(int num, TWORD type, struct swents **p, int n)
582 {
583 	return 0;
584 }
585 
586 NODE *
587 builtin_return_address(const struct bitable *bt, NODE *a)
588 {
589 	int nframes;
590 	NODE *f;
591 
592 	if (a->n_op != ICON)
593 		goto bad;
594 
595 	nframes = (int)a->n_lval;
596 
597 	tfree(a);
598 
599 	f = block(REG, NIL, NIL, PTR+VOID, 0, 0);
600 	regno(f) = FPREG;
601 
602 	while (nframes--)
603 		f = block(UMUL, f, NIL, PTR+VOID, 0, 0);
604 
605 	f = block(PLUS, f, bcon(4), INCREF(PTR+VOID), 0, 0);
606 	f = buildtree(UMUL, f, NIL);
607 
608 	return f;
609 bad:
610 	uerror("bad argument to __builtin_return_address");
611 	return bcon(0);
612 }
613 
614 NODE *
615 builtin_frame_address(const struct bitable *bt, NODE *a)
616 {
617 	int nframes;
618 	NODE *f;
619 
620 	if (a->n_op != ICON)
621 		goto bad;
622 
623 	nframes = (int)a->n_lval;
624 
625 	tfree(a);
626 
627 	f = block(REG, NIL, NIL, PTR+VOID, 0, 0);
628 	regno(f) = FPREG;
629 
630 	while (nframes--)
631 		f = block(UMUL, f, NIL, PTR+VOID, 0, 0);
632 
633 	return f;
634 bad:
635 	uerror("bad argument to __builtin_frame_address");
636 	return bcon(0);
637 }
638 
639 /*
640  * Return "canonical frame address".
641  */
642 NODE *
643 builtin_cfa(const struct bitable *bt, NODE *a)
644 {
645 	uerror("missing builtin_cfa");
646 	return bcon(0);
647 }
648 
649