* src/hc08/gen.c (hc08_emitDebuggerSymbol),
[fw/sdcc] / src / mcs51 / main.c
1 /** @file main.c
2     mcs51 specific general functions.
3
4     Note that mlh prepended _mcs51_ on the static functions.  Makes
5     it easier to set a breakpoint using the debugger.
6 */
7 #include "common.h"
8 #include "main.h"
9 #include "ralloc.h"
10 #include "gen.h"
11 #include "../SDCCutil.h"
12
13 static char _defaultRules[] =
14 {
15 #include "peeph.rul"
16 };
17
18 /* list of key words used by msc51 */
19 static char *_mcs51_keywords[] =
20 {
21   "at",
22   "bit",
23   "code",
24   "critical",
25   "data",
26   "far",
27   "idata",
28   "interrupt",
29   "near",
30   "pdata",
31   "reentrant",
32   "sfr",
33   "sbit",
34   "using",
35   "xdata",
36   "_data",
37   "_code",
38   "_generic",
39   "_near",
40   "_xdata",
41   "_pdata",
42   "_idata",
43   "_naked",
44   "_overlay",
45   NULL
46 };
47
48
49
50 void mcs51_assignRegisters (eBBlock ** ebbs, int count);
51
52 static int regParmFlg = 0;      /* determine if we can register a parameter */
53
54 static void
55 _mcs51_init (void)
56 {
57   asm_addTree (&asm_asxxxx_mapping);
58 }
59
60 static void
61 _mcs51_reset_regparm ()
62 {
63   regParmFlg = 0;
64 }
65
66 static int
67 _mcs51_regparm (sym_link * l)
68 {
69     if (options.parms_in_bank1 == 0) {
70         /* simple can pass only the first parameter in a register */
71         if (regParmFlg)
72             return 0;
73
74         regParmFlg = 1;
75         return 1;
76     } else {
77         int size = getSize(l);
78         int remain ;
79
80         /* first one goes the usual way to DPTR */
81         if (regParmFlg == 0) {
82             regParmFlg += 4 ;
83             return 1;
84         }
85         /* second one onwards goes to RB1_0 thru RB1_7 */
86         remain = regParmFlg - 4;
87         if (size > (8 - remain)) {
88             regParmFlg = 12 ;
89             return 0;
90         }
91         regParmFlg += size ;
92         return regParmFlg - size + 1;   
93     }
94 }
95
96 static bool
97 _mcs51_parseOptions (int *pargc, char **argv, int *i)
98 {
99   /* TODO: allow port-specific command line options to specify
100    * segment names here.
101    */
102   return FALSE;
103 }
104
105 static void
106 _mcs51_finaliseOptions (void)
107 {
108   if (options.noXinitOpt) {
109     port->genXINIT=0;
110   }
111
112   if (options.model == MODEL_LARGE) {
113       port->mem.default_local_map = xdata;
114       port->mem.default_globl_map = xdata;
115     }
116   else
117     {
118       port->mem.default_local_map = data;
119       port->mem.default_globl_map = data;
120     }
121
122   if (options.parms_in_bank1) {
123       addSet(&preArgvSet, Safe_strdup("-DSDCC_PARMS_IN_BANK1"));
124   }
125 }
126
127 static void
128 _mcs51_setDefaultOptions (void)
129 {
130 }
131
132 static const char *
133 _mcs51_getRegName (struct regs *reg)
134 {
135   if (reg)
136     return reg->name;
137   return "err";
138 }
139
140 static void
141 _mcs51_genAssemblerPreamble (FILE * of)
142 {
143     if (options.parms_in_bank1) {
144         int i ;
145         for (i=0; i < 8 ; i++ )
146             fprintf (of,"b1_%d = 0x%x \n",i,8+i);
147     }
148   
149 }
150
151 /* Generate interrupt vector table. */
152 static int
153 _mcs51_genIVT (FILE * of, symbol ** interrupts, int maxInterrupts)
154 {
155   return FALSE;
156 }
157
158 static void       
159 _mcs51_genExtraAreas(FILE *of, bool hasMain)
160 {
161   tfprintf (of, "\t!area\n", port->mem.code_name);
162   tfprintf (of, "\t!area\n", "GSINIT0 (CODE)");
163   tfprintf (of, "\t!area\n", "GSINIT1 (CODE)");
164   tfprintf (of, "\t!area\n", "GSINIT2 (CODE)");
165   tfprintf (of, "\t!area\n", "GSINIT3 (CODE)");
166   tfprintf (of, "\t!area\n", "GSINIT4 (CODE)");
167   tfprintf (of, "\t!area\n", "GSINIT5 (CODE)");
168 }
169
170 static void
171 _mcs51_genInitStartup (FILE *of)
172 {
173   tfprintf (of, "\t!global\n", "__sdcc_gsinit_startup");
174   tfprintf (of, "\t!global\n", "__sdcc_program_startup");
175   tfprintf (of, "\t!global\n", "__start__stack");
176   
177   if (options.useXstack)
178     {
179       tfprintf (of, "\t!global\n", "__sdcc_init_xstack");
180       tfprintf (of, "\t!global\n", "__start__xstack");
181       fprintf (of, "__start__xstack = 0x%04x", options.xdata_loc);
182     }
183
184   // if the port can copy the XINIT segment to XISEG
185   if (port->genXINIT)
186     {
187       port->genXINIT(of);
188     }
189   
190   if (!getenv("SDCC_NOGENRAMCLEAR"))
191     tfprintf (of, "\t!global\n", "__mcs51_genRAMCLEAR");
192 }
193
194
195 /* Generate code to copy XINIT to XISEG */
196 static void _mcs51_genXINIT (FILE * of) {
197   tfprintf (of, "\t!global\n", "__mcs51_genXINIT");
198   
199   if (!getenv("SDCC_NOGENRAMCLEAR"))
200     tfprintf (of, "\t!global\n", "__mcs51_genXRAMCLEAR");
201 }
202
203
204 /* Do CSE estimation */
205 static bool cseCostEstimation (iCode *ic, iCode *pdic)
206 {
207     operand *result = IC_RESULT(ic);
208     sym_link *result_type = operandType(result);
209
210     /* if it is a pointer then return ok for now */
211     if (IC_RESULT(ic) && IS_PTR(result_type)) return 1;
212     
213     /* if bitwise | add & subtract then no since mcs51 is pretty good at it 
214        so we will cse only if they are local (i.e. both ic & pdic belong to
215        the same basic block */
216     if (IS_BITWISE_OP(ic) || ic->op == '+' || ic->op == '-') {
217         /* then if they are the same Basic block then ok */
218         if (ic->eBBlockNum == pdic->eBBlockNum) return 1;
219         else return 0;
220     }
221         
222     /* for others it is cheaper to do the cse */
223     return 1;
224 }
225
226 /* Indicate which extended bit operations this port supports */
227 static bool
228 hasExtBitOp (int op, int size)
229 {
230   if (op == RRC
231       || op == RLC
232       || op == GETHBIT
233       || (op == SWAP && size <= 2)
234      )
235     return TRUE;
236   else
237     return FALSE;
238 }
239
240 /* Indicate the expense of an access to an output storage class */
241 static int
242 oclsExpense (struct memmap *oclass)
243 {
244   if (IN_FARSPACE(oclass))
245     return 1;
246     
247   return 0;
248 }
249
250
251
252 static int
253 instructionSize(char *inst, char *op1, char *op2)
254 {
255   #define ISINST(s) (strncmp(inst, (s), sizeof(s)-1) == 0)
256   #define IS_A(s) (*(s) == 'a' && *(s+1) == '\0')
257   #define IS_C(s) (*(s) == 'c' && *(s+1) == '\0')
258   #define IS_Rn(s) (*(s) == 'r' && *(s+1) >= '0' && *(s+1) <= '7')
259   #define IS_atRi(s) (*(s) == '@' && *(s+1) == 'r')
260
261   /* Based on the current (2003-08-22) code generation for the
262      small library, the top instruction probability is:
263    
264        57% mov/movx/movc
265         6% push
266         6% pop
267         4% inc
268         4% lcall
269         4% add
270         3% clr
271         2% subb
272   */
273   /* mov, push, & pop are the 69% of the cases. Check them first! */
274   if (ISINST ("mov"))
275     {
276       if (*(inst+3)=='x') return 1; /* movx */
277       if (*(inst+3)=='c') return 1; /* movc */
278       if (IS_C (op1) || IS_C (op2)) return 2;
279       if (IS_A (op1))
280         {
281           if (IS_Rn (op2) || IS_atRi (op2)) return 1;
282           return 2;
283         }
284       if (IS_Rn(op1) || IS_atRi(op1))
285         {
286           if (IS_A(op2)) return 1;
287           return 2;
288         }
289       if (strcmp (op1, "dptr") == 0) return 3;
290       if (IS_A (op2) || IS_Rn (op2) || IS_atRi (op2)) return 2;
291       return 3;
292     }
293   
294   if (ISINST ("push")) return 2;
295   if (ISINST ("pop")) return 2;
296
297   if (ISINST ("lcall")) return 3;
298   if (ISINST ("ret")) return 1;
299   if (ISINST ("ljmp")) return 3;
300   if (ISINST ("sjmp")) return 2;
301   if (ISINST ("rlc")) return 1;
302   if (ISINST ("rrc")) return 1;
303   if (ISINST ("rl")) return 1;
304   if (ISINST ("rr")) return 1;
305   if (ISINST ("swap")) return 1;
306   if (ISINST ("jc")) return 2;
307   if (ISINST ("jnc")) return 2;
308   if (ISINST ("jb")) return 3;
309   if (ISINST ("jnb")) return 3;
310   if (ISINST ("jbc")) return 3;
311   if (ISINST ("jmp")) return 1; // always jmp @a+dptr
312   if (ISINST ("jz")) return 2;
313   if (ISINST ("jnz")) return 2;
314   if (ISINST ("cjne")) return 3;
315   if (ISINST ("mul")) return 1;
316   if (ISINST ("div")) return 1;
317   if (ISINST ("da")) return 1;
318   if (ISINST ("xchd")) return 1;
319   if (ISINST ("reti")) return 1;
320   if (ISINST ("nop")) return 1;
321   if (ISINST ("acall")) return 2;
322   if (ISINST ("ajmp")) return 2;
323
324     
325   if (ISINST ("add") || ISINST ("addc") || ISINST ("subb") || ISINST ("xch"))
326     {
327       if (IS_Rn(op2) || IS_atRi(op2)) return 1;
328       return 2;
329     }
330   if (ISINST ("inc") || ISINST ("dec"))
331     {
332       if (IS_A(op1) || IS_Rn(op1) || IS_atRi(op1)) return 1;
333       if (strcmp(op1, "dptr") == 0) return 1;
334       return 2;
335     }
336   if (ISINST ("anl") || ISINST ("orl") || ISINST ("xrl"))
337     {
338       if (IS_C(op1)) return 2;
339       if (IS_A(op1))
340         {
341           if (IS_Rn(op2) || IS_atRi(op2)) return 1;
342           return 2;
343         }
344       else
345         {
346           if (IS_A(op2)) return 2;
347           return 3;
348         }
349     }
350   if (ISINST ("clr") || ISINST ("setb") || ISINST ("cpl"))
351     {
352       if (IS_A(op1) || IS_C(op1)) return 1;
353       return 2;
354     }
355   if (ISINST ("djnz"))
356     {
357       if (IS_Rn(op1)) return 2;
358       return 3;
359     }
360
361   /* If the instruction is unrecognized, we shouldn't try to optimize. */
362   /* Return a large value to discourage optimization.                  */
363   return 999;
364 }
365
366 static asmLineNode *
367 newAsmLineNode (void)
368 {
369   asmLineNode *aln;
370
371   aln = Safe_alloc ( sizeof (asmLineNode));
372   aln->size = 0;
373   aln->regsRead = NULL;
374   aln->regsWritten = NULL;
375   
376   return aln;
377 }
378
379
380 typedef struct mcs51operanddata
381   {
382     char name[6];
383     int regIdx1;
384     int regIdx2;
385   }
386 mcs51operanddata;
387
388 static mcs51operanddata mcs51operandDataTable[] =
389   {
390     {"a", A_IDX, -1},
391     {"ab", A_IDX, B_IDX},
392     {"ac", CND_IDX, -1},
393     {"acc", A_IDX, -1},
394     {"ar0", R0_IDX, -1},
395     {"ar1", R1_IDX, -1},
396     {"ar2", R2_IDX, -1},
397     {"ar3", R3_IDX, -1},
398     {"ar4", R4_IDX, -1},
399     {"ar5", R5_IDX, -1},
400     {"ar6", R6_IDX, -1},
401     {"ar7", R7_IDX, -1},
402     {"b", B_IDX, -1},
403     {"c", CND_IDX, -1},
404     {"cy", CND_IDX, -1},
405     {"dph", DPH_IDX, -1},
406     {"dpl", DPL_IDX, -1},
407     {"dptr", DPL_IDX, DPH_IDX},
408     {"f0", CND_IDX, -1},
409     {"f1", CND_IDX, -1},
410     {"ov", CND_IDX, -1},
411     {"p", CND_IDX, -1},
412     {"psw", CND_IDX, -1},
413     {"r0", R0_IDX, -1},
414     {"r1", R1_IDX, -1},
415     {"r2", R2_IDX, -1},
416     {"r3", R3_IDX, -1},
417     {"r4", R4_IDX, -1},
418     {"r5", R5_IDX, -1},
419     {"r6", R6_IDX, -1},
420     {"r7", R7_IDX, -1},
421   };
422
423 static int
424 mcs51operandCompare (const void *key, const void *member)
425 {
426   return strcmp((const char *)key, ((mcs51operanddata *)member)->name);
427 }
428
429 static void      
430 updateOpRW (asmLineNode *aln, char *op, char *optype)
431 {
432   mcs51operanddata *opdat;
433   char *dot;
434   
435   dot = strchr(op, '.');
436   if (dot)
437     *dot = '\0';
438
439   opdat = bsearch (op, mcs51operandDataTable,
440                    sizeof(mcs51operandDataTable)/sizeof(mcs51operanddata),
441                    sizeof(mcs51operanddata), mcs51operandCompare);
442   
443   if (opdat && strchr(optype,'r'))
444     {
445       if (opdat->regIdx1 >= 0)
446         aln->regsRead = bitVectSetBit (aln->regsRead, opdat->regIdx1);
447       if (opdat->regIdx2 >= 0)
448         aln->regsRead = bitVectSetBit (aln->regsRead, opdat->regIdx2);
449     }
450   if (opdat && strchr(optype,'w'))
451     {
452       if (opdat->regIdx1 >= 0)
453         aln->regsWritten = bitVectSetBit (aln->regsWritten, opdat->regIdx1);
454       if (opdat->regIdx2 >= 0)
455         aln->regsWritten = bitVectSetBit (aln->regsWritten, opdat->regIdx2);
456     }
457   if (op[0] == '@')
458     {
459       if (!strcmp(op, "@r0"))
460         aln->regsRead = bitVectSetBit (aln->regsRead, R0_IDX);
461       if (!strcmp(op, "@r1"))
462         aln->regsRead = bitVectSetBit (aln->regsRead, R1_IDX);
463       if (strstr(op, "dptr"))
464         {
465           aln->regsRead = bitVectSetBit (aln->regsRead, DPL_IDX);
466           aln->regsRead = bitVectSetBit (aln->regsRead, DPH_IDX);
467         }
468       if (strstr(op, "a+"))
469         aln->regsRead = bitVectSetBit (aln->regsRead, A_IDX);
470     }
471 }
472
473 typedef struct mcs51opcodedata
474   {
475     char name[6];
476     char class[3];
477     char pswtype[3];
478     char op1type[3];
479     char op2type[3];
480   }
481 mcs51opcodedata;
482
483 static mcs51opcodedata mcs51opcodeDataTable[] =
484   {
485     {"acall","j", "",   "",   ""},
486     {"ajmp", "j", "",   "",   ""},
487     {"add",  "",  "w",  "rw", "r"},
488     {"addc", "",  "rw", "rw", "r"},
489     {"anl",  "",  "",   "rw", "r"},
490     {"cjne", "j", "w",  "r",  "r"},
491     {"clr",  "",  "",   "w",  ""},
492     {"cpl",  "",  "",   "rw", ""},
493     {"da",   "",  "rw", "rw", ""},
494     {"dec",  "",  "",   "rw", ""},
495     {"div",  "",  "w",  "rw", ""},
496     {"djnz", "j", "",  "rw",  ""},
497     {"inc",  "",  "",   "rw", ""},
498     {"jb",   "j", "",   "r",  ""},
499     {"jbc",  "j", "",  "rw",  ""},
500     {"jc",   "j", "",   "",   ""},
501     {"jmp",  "j", "",  "",    ""},
502     {"jnb",  "j", "",   "r",  ""},
503     {"jnc",  "j", "",   "",   ""},
504     {"jnz",  "j", "",  "",    ""},
505     {"jz",   "j", "",  "",    ""},
506     {"lcall","j", "",   "",   ""},
507     {"ljmp", "j", "",   "",   ""},
508     {"mov",  "",  "",   "w",  "r"},
509     {"movc", "",  "",   "w",  "r"},
510     {"movx", "",  "",   "w",  "r"},
511     {"mul",  "",  "w",  "rw", ""},
512     {"nop",  "",  "",   "",   ""},
513     {"orl",  "",  "",   "rw", "r"},
514     {"pop",  "",  "",   "w",  ""},
515     {"push", "",  "",   "r",  ""},
516     {"ret",  "j", "",   "",   ""},
517     {"reti", "j", "",   "",   ""},
518     {"rl",   "",  "",   "rw", ""},
519     {"rlc",  "",  "rw", "rw", ""},
520     {"rr",   "",  "",   "rw", ""},
521     {"rrc",  "",  "rw", "rw", ""},
522     {"setb", "",  "",   "w",  ""},
523     {"sjmp", "j", "",   "",   ""},
524     {"subb", "",  "rw", "rw", "r"},
525     {"swap", "",  "",   "rw", ""},
526     {"xch",  "",  "",   "rw", "rw"},
527     {"xchd", "",  "",   "rw", "rw"},
528     {"xrl",  "",  "",   "rw", "r"},
529   };
530   
531 static int
532 mcs51opcodeCompare (const void *key, const void *member)
533 {
534   return strcmp((const char *)key, ((mcs51opcodedata *)member)->name);
535 }
536
537 static asmLineNode *
538 asmLineNodeFromLineNode (lineNode *ln)
539 {
540   asmLineNode *aln = newAsmLineNode();
541   char *op, op1[256], op2[256];
542   int opsize;
543   const char *p;
544   char inst[8];
545   mcs51opcodedata *opdat;
546
547   p = ln->line;
548   
549   while (*p && isspace(*p)) p++;
550   for (op = inst, opsize=1; *p; p++)
551     {
552       if (isspace(*p) || *p == ';' || *p == ':' || *p == '=')
553         break;
554       else
555         if (opsize < sizeof(inst))
556           *op++ = tolower(*p), opsize++;
557     }
558   *op = '\0';
559
560   if (*p == ';' || *p == ':' || *p == '=')
561     return aln;
562     
563   while (*p && isspace(*p)) p++;
564   if (*p == '=')
565     return aln;
566
567   for (op = op1, opsize=1; *p && *p != ','; p++)
568     {
569       if (!isspace(*p) && opsize < sizeof(op1))
570         *op++ = tolower(*p), opsize++;
571     }
572   *op = '\0';
573   
574   if (*p == ',') p++;
575   for (op = op2, opsize=1; *p && *p != ','; p++)
576     {
577       if (!isspace(*p) && opsize < sizeof(op2))
578         *op++ = tolower(*p), opsize++;
579     }
580   *op = '\0';
581
582   aln->size = instructionSize(inst, op1, op2);
583
584   aln->regsRead = newBitVect (END_IDX);
585   aln->regsWritten = newBitVect (END_IDX);
586
587   opdat = bsearch (inst, mcs51opcodeDataTable,
588                    sizeof(mcs51opcodeDataTable)/sizeof(mcs51opcodedata),
589                    sizeof(mcs51opcodedata), mcs51opcodeCompare);
590
591   if (opdat)
592     {
593       updateOpRW (aln, op1, opdat->op1type);
594       updateOpRW (aln, op2, opdat->op2type);
595       if (strchr(opdat->pswtype,'r'))
596         aln->regsRead = bitVectSetBit (aln->regsRead, CND_IDX);
597       if (strchr(opdat->pswtype,'w'))
598         aln->regsWritten = bitVectSetBit (aln->regsWritten, CND_IDX);
599     }
600
601   return aln;
602 }
603
604 static int
605 getInstructionSize (lineNode *line)
606 {
607   if (!line->aln)
608     line->aln = asmLineNodeFromLineNode (line);
609   
610   return line->aln->size;
611 }
612
613 static bitVect *
614 getRegsRead (lineNode *line)
615 {
616   if (!line->aln)
617     line->aln = asmLineNodeFromLineNode (line);
618   
619   return line->aln->regsRead;
620 }
621
622 static bitVect *
623 getRegsWritten (lineNode *line)
624 {
625   if (!line->aln)
626     line->aln = asmLineNodeFromLineNode (line);
627   
628   return line->aln->regsWritten;
629 }
630
631
632 /** $1 is always the basename.
633     $2 is always the output file.
634     $3 varies
635     $l is the list of extra options that should be there somewhere...
636     MUST be terminated with a NULL.
637 */
638 static const char *_linkCmd[] =
639 {
640   "aslink", "-nf", "\"$1\"", NULL
641 };
642
643 /* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */
644 static const char *_asmCmd[] =
645 {
646   "asx8051", "$l", "$3", "\"$1.asm\"", NULL
647 };
648
649 /* Globals */
650 PORT mcs51_port =
651 {
652   TARGET_ID_MCS51,
653   "mcs51",
654   "MCU 8051",                   /* Target name */
655   NULL,                         /* Processor name */
656   {
657     glue,
658     TRUE,                       /* Emit glue around main */
659     MODEL_SMALL | MODEL_LARGE,
660     MODEL_SMALL
661   },
662   {
663     _asmCmd,
664     NULL,
665     "-plosgffc",                /* Options with debug */
666     "-plosgff",                 /* Options without debug */
667     0,
668     ".asm",
669     NULL                        /* no do_assemble function */
670   },
671   {
672     _linkCmd,
673     NULL,
674     NULL,
675     ".rel",
676     1
677   },
678   {
679     _defaultRules,
680     getInstructionSize,
681     getRegsRead,
682     getRegsWritten
683   },
684   {
685         /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
686     1, 2, 2, 4, 1, 2, 3, 1, 4, 4
687   },
688   {
689     "XSEG    (XDATA)",
690     "STACK   (DATA)",
691     "CSEG    (CODE)",
692     "DSEG    (DATA)",
693     "ISEG    (DATA)",
694     "XSEG    (XDATA)",
695     "BSEG    (BIT)",
696     "RSEG    (DATA)",
697     "GSINIT  (CODE)",
698     "OSEG    (OVR,DATA)",
699     "GSFINAL (CODE)",
700     "HOME    (CODE)",
701     "XISEG   (XDATA)", // initialized xdata
702     "XINIT   (CODE)", // a code copy of xiseg
703     NULL,
704     NULL,
705     1
706   },
707   { _mcs51_genExtraAreas, NULL },
708   {
709     +1, 0, 4, 1, 1, 0
710   },
711     /* mcs51 has an 8 bit mul */
712   {
713     1, -1
714   },
715   {
716     mcs51_emitDebuggerSymbol
717   },
718   "_",
719   _mcs51_init,
720   _mcs51_parseOptions,
721   NULL,
722   _mcs51_finaliseOptions,
723   _mcs51_setDefaultOptions,
724   mcs51_assignRegisters,
725   _mcs51_getRegName,
726   _mcs51_keywords,
727   _mcs51_genAssemblerPreamble,
728   NULL,                         /* no genAssemblerEnd */
729   _mcs51_genIVT,
730   _mcs51_genXINIT,
731   _mcs51_genInitStartup,
732   _mcs51_reset_regparm,
733   _mcs51_regparm,
734   NULL,
735   NULL,
736   NULL,
737   hasExtBitOp,                  /* hasExtBitOp */
738   oclsExpense,                  /* oclsExpense */
739   FALSE,
740   TRUE,                         /* little endian */
741   0,                            /* leave lt */
742   0,                            /* leave gt */
743   1,                            /* transform <= to ! > */
744   1,                            /* transform >= to ! < */
745   1,                            /* transform != to !(a == b) */
746   0,                            /* leave == */
747   FALSE,                        /* No array initializer support. */
748   cseCostEstimation,
749   NULL,                         /* no builtin functions */
750   GPOINTER,                     /* treat unqualified pointers as "generic" pointers */
751   1,                            /* reset labelKey to 1 */
752   1,                            /* globals & local static allowed */
753   PORT_MAGIC
754 };