]> Shamusworld >> Repos - rmac/blob - expr.c
d1b9ac2e08e50114af26a0272cc93f5e0123348b
[rmac] / expr.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // EXPR.C - Expression Analyzer
4 // Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source utilised with the kind permission of Landon Dyer
7 //
8
9 #include "expr.h"
10 #include "direct.h"
11 #include "error.h"
12 #include "listing.h"
13 #include "mach.h"
14 #include "procln.h"
15 #include "riscasm.h"
16 #include "sect.h"
17 #include "symbol.h"
18 #include "token.h"
19
20 #define DEF_KW                                          // Declare keyword values 
21 #include "kwtab.h"                                      // Incl generated keyword tables & defs
22
23 // N.B.: The size of tokenClass should be identical to the largest value of
24 //       a token; we're assuming 256 but not 100% sure!
25 static char tokenClass[256];            // Generated table of token classes
26 static VALUE evstk[EVSTACKSIZE];        // Evaluator value stack
27 static WORD evattr[EVSTACKSIZE];        // Evaluator attribute stack
28
29 // Token-class initialization list
30 char itokcl[] = {
31         0,                                                              // END
32         CONST, SYMBOL, 0,                               // ID 
33         '(', '[', '{', 0,                               // OPAR
34         ')', ']', '}', 0,                               // CPAR 
35         CR_DEFINED, CR_REFERENCED,              // SUNARY (special unary)
36         CR_STREQ, CR_MACDEF,
37         CR_DATE, CR_TIME, 0,
38         '!', '~', UNMINUS, 0,                   // UNARY
39         '*', '/', '%', 0,                               // MULT 
40         '+', '-', 0,                                    // ADD 
41         SHL, SHR, 0,                                    // SHIFT 
42         LE, GE, '<', '>', NE, '=', 0,   // REL 
43         '&', 0,                                                 // AND 
44         '^', 0,                                                 // XOR 
45         '|', 0,                                                 // OR 
46         1                                                               // (the end) 
47 };
48
49 const char missym_error[] = "missing symbol";
50 const char str_error[] = "missing symbol or string";
51
52 // Convert expression to postfix
53 static TOKEN * evalTokenBuffer;         // Deposit tokens here (this is really a
54                                                                         // pointer to exprbuf from direct.c)
55                                                                         // (Can also be from others, like
56                                                                         // riscasm.c)
57 static symbolNum;                                       // Pointer to the entry in symbolPtr[]
58
59
60 //
61 // Obtain a string value
62 //
63 static VALUE str_value(char * p)
64 {
65         VALUE v;
66
67         for(v=0; *p; p++)
68                 v = (v << 8) | (*p & 0xFF);
69
70         return v;
71 }
72
73
74 //
75 // Initialize expression analyzer
76 //
77 void InitExpression(void)
78 {
79         int i;
80         char * p;
81
82         // Initialize token-class table (all set to END)
83         for(i=0; i<256; i++)
84                 tokenClass[i] = END;
85
86         for(i=0, p=itokcl; *p!=1; p++)
87         {
88                 if (*p == 0)
89                         i++;
90                 else 
91                         tokenClass[(int)(*p)] = (char)i;
92         }
93
94         symbolNum = 0;
95 }
96
97
98 //
99 // Binary operators (all the same precedence)
100 //
101 int expr0(void)
102 {
103         TOKEN t;
104
105         if (expr1() != OK)
106                 return ERROR;
107         
108         while (tokenClass[*tok] >= MULT)
109         {
110                 t = *tok++;
111
112                 if (expr1() != OK)
113                         return ERROR;
114
115                 *evalTokenBuffer++ = t;
116         }
117
118         return OK;
119 }
120
121
122 //
123 // Unary operators (detect unary '-')
124 //
125 int expr1(void)
126 {
127         int class;
128         TOKEN t;
129         SYM * sy;
130         char * p, * p2;
131         WORD w;
132         int j;
133
134         class = tokenClass[*tok];
135
136         if (*tok == '-' || class == UNARY)
137         {
138                 t = *tok++;
139
140                 if (expr2() != OK)
141                         return ERROR;
142
143                 if (t == '-')
144                         t = UNMINUS;
145
146                 *evalTokenBuffer++ = t;
147         }
148         else if (class == SUNARY)
149         {
150                 switch ((int)*tok++)
151                 {
152                 case CR_TIME:
153                         *evalTokenBuffer++ = CONST;
154                         *evalTokenBuffer++ = dos_time();
155                         break;
156                 case CR_DATE:
157                         *evalTokenBuffer++ = CONST;
158                         *evalTokenBuffer++ = dos_date();
159                         break;
160                 case CR_MACDEF:                                    // ^^macdef <macro-name>
161                         if (*tok++ != SYMBOL)
162                                 return error(missym_error);
163
164                         p = string[*tok++];
165                         w = (lookup(p, MACRO, 0) == NULL ? 0 : 1);
166                         *evalTokenBuffer++ = CONST;
167                         *evalTokenBuffer++ = (TOKEN)w;
168                         break;
169                 case CR_DEFINED:
170                         w = DEFINED;
171                         goto getsym;
172                 case CR_REFERENCED:
173                         w = REFERENCED;
174 getsym:
175                         if (*tok++ != SYMBOL)
176                                 return error(missym_error);
177
178                         p = string[*tok++];
179                         j = (*p == '.' ? curenv : 0);
180                         w = ((sy = lookup(p, LABEL, j)) != NULL && (sy->sattr & w) ? 1 : 0);
181                         *evalTokenBuffer++ = CONST;
182                         *evalTokenBuffer++ = (TOKEN)w;
183                         break;
184                 case CR_STREQ:
185                         if (*tok != SYMBOL && *tok != STRING)
186                                 return error(str_error);
187
188                         p = string[tok[1]];
189                         tok +=2;
190
191                         if (*tok++ != ',')
192                                 return error(comma_error);
193
194                         if (*tok != SYMBOL && *tok != STRING)
195                                 return error(str_error);
196
197                         p2 = string[tok[1]];
198                         tok += 2;
199
200                         w = (WORD)(!strcmp(p, p2));
201                         *evalTokenBuffer++ = CONST;
202                         *evalTokenBuffer++ = (TOKEN)w;
203                         break;
204                 }
205         }
206         else 
207                 return expr2();
208
209         return OK;
210 }
211
212
213 //
214 // Terminals (CONSTs) and parenthesis grouping
215 //
216 int expr2(void)
217 {
218         char * p;
219         SYM * sy;
220         int j;
221
222         switch ((int)*tok++)
223         {
224         case CONST:
225                 *evalTokenBuffer++ = CONST;
226                 *evalTokenBuffer++ = *tok++;
227                 break;
228         case SYMBOL:
229                 p = string[*tok++];
230                 j = (*p == '.' ? curenv : 0);
231                 sy = lookup(p, LABEL, j);
232
233                 if (sy == NULL)
234                         sy = NewSymbol(p, LABEL, j);
235
236                 // Check register bank usage
237                 if (sy->sattre & EQUATEDREG)
238                 {
239                         if ((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok)   
240                                 warns("equated symbol \'%s\' cannot be used in register bank 0", sy->sname);
241
242                         if ((regbank == BANK_1) && (sy->sattre & BANK_0) && !altbankok)
243                                 warns("equated symbol \'%s\' cannot be used in register bank 1", sy->sname);
244                 }
245
246                 *evalTokenBuffer++ = SYMBOL;
247                 *evalTokenBuffer++ = symbolNum;
248                 symbolPtr[symbolNum] = sy;
249                 symbolNum++;
250                 break;
251         case STRING:
252                 *evalTokenBuffer++ = CONST;
253                 *evalTokenBuffer++ = str_value(string[*tok++]);
254                 break;
255         case '(':
256                 if (expr0() != OK)
257                         return ERROR;
258
259                 if (*tok++ != ')')
260                         return error("missing close parenthesis ')'");
261
262                 break;
263         case '[':
264                 if (expr0() != OK)
265                         return ERROR;
266
267                 if (*tok++ != ']')
268                         return error("missing close parenthesis ']'");
269
270                 break;
271         case '$':
272                 *evalTokenBuffer++ = ACONST;                            // Attributed const
273                 *evalTokenBuffer++ = sloc;                                      // Current location
274                 *evalTokenBuffer++ = cursect | DEFINED;         // Store attribs
275                 break;
276         case '*':
277                 *evalTokenBuffer++ = ACONST;                            // Attributed const
278
279                 // pcloc == location at start of line
280                 *evalTokenBuffer++ = (orgactive ? orgaddr : pcloc);
281                 // '*' takes attributes of current section, not ABS!
282                 *evalTokenBuffer++ = cursect | DEFINED;
283                 break;
284         default:
285                 return error("bad expression");
286         }
287
288         return OK;
289 }
290
291
292 //
293 // Recursive-descent expression analyzer (with some simple speed hacks)
294 //
295 int expr(TOKEN * otk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
296 {
297         // Passed in values (once derefenced, that is) can all be zero. They are
298         // there so that the expression analyzer can fill them in as needed. The
299         // expression analyzer gets its input from the global token pointer "tok",
300         // and not from anything passed in by the user.
301         SYM * symbol;
302         char * p;
303         int j;
304
305         evalTokenBuffer = otk;  // Set token pointer to 'exprbuf' (direct.c)
306                                                         // Also set in various other places too (riscasm.c, e.g.)
307
308 //printf("expr(): tokens 0-2: %i %i %i (%c %c %c); tc[2] = %i\n", tok[0], tok[1], tok[2], tok[0], tok[1], tok[2], tokenClass[tok[2]]);
309         // Optimize for single constant or single symbol.
310         // Shamus: Subtle bug here. EOL token is 101; if you have a constant token
311         //         followed by the value 101, it will trigger a bad evaluation here.
312         //         This is probably a really bad assumption to be making here...!
313         //         (assuming tok[1] == EOL is a single token that is)
314         //         Seems that even other tokens (SUNARY type) can fuck this up too.
315 //      if ((tok[1] == EOL)
316         if ((tok[1] == EOL && (tok[0] != CONST && tokenClass[tok[0]] != SUNARY))
317                 || (((*tok == CONST || *tok == SYMBOL) || (*tok >= KW_R0 && *tok <= KW_R31))
318                 && (tokenClass[tok[2]] < UNARY)))
319         {
320                 if (*tok >= KW_R0 && *tok <= KW_R31)
321                 {
322                         *evalTokenBuffer++ = CONST;
323                         *evalTokenBuffer++ = *a_value = (*tok - KW_R0);
324                         *a_attr = ABS | DEFINED;
325
326                         if (a_esym != NULL)
327                                 *a_esym = NULL;
328
329                         tok++;
330                 }
331                 else if (*tok == CONST)
332                 {
333                         *evalTokenBuffer++ = CONST;
334                         *evalTokenBuffer++ = *a_value = tok[1];
335                         *a_attr = ABS | DEFINED;
336
337                         if (a_esym != NULL)
338                                 *a_esym = NULL;
339
340                         tok += 2;
341 //printf("Quick eval in expr(): CONST = %i, tokenClass[tok[2]] = %i\n", *a_value, tokenClass[*tok]);
342                 }
343                 else if (*tok == '*')
344                 {
345                         *evalTokenBuffer++ = CONST;
346
347                         if (orgactive)
348                                 *evalTokenBuffer++ = *a_value = orgaddr;
349                         else
350                                 *evalTokenBuffer++ = *a_value = pcloc;
351
352                         // '*' takes attributes of current section, not ABS!
353                         *a_attr = cursect | DEFINED;
354
355                         if (a_esym != NULL)
356                                 *a_esym = NULL;
357
358                         tok++;
359                 }
360                 else if (*tok == STRING || *tok == SYMBOL)
361                 {
362                         p = string[tok[1]];
363                         j = (*p == '.' ? curenv : 0);
364                         symbol = lookup(p, LABEL, j);
365 #if 0
366 printf("eval: Looking up symbol (%s) [=%08X]\n", p, symbol);
367 if (symbol)
368         printf("      attr=%04X, attre=%08X, val=%i, name=%s\n", symbol->sattr, symbol->sattre, symbol->svalue, symbol->sname);
369 #endif
370
371                         if (symbol == NULL)
372                                 symbol = NewSymbol(p, LABEL, j);
373
374                         symbol->sattr |= REFERENCED;
375
376                         // Check for undefined register equates, but only if it's not part
377                         // of a #<SYMBOL> construct, as it could be that the label that's
378                         // been undefined may later be used as an address label--which
379                         // means it will be fixed up later, and thus, not an error.
380                         if ((symbol->sattre & UNDEF_EQUR) && !riscImmTokenSeen)
381                         {
382                                 errors("undefined register equate '%s'", symbol->sname);
383 //if we return right away, it returns some spurious errors...
384 //                              return ERROR;
385                         }
386
387                         // Check register bank usage
388                         if (symbol->sattre & EQUATEDREG)
389                         {
390                                 if ((regbank == BANK_0) && (symbol->sattre & BANK_1) && !altbankok)   
391                                         warns("equated symbol '%s' cannot be used in register bank 0", symbol->sname);
392
393                                 if ((regbank == BANK_1) && (symbol->sattre & BANK_0) && !altbankok)
394                                         warns("equated symbol '%s' cannot be used in register bank 1", symbol->sname);
395                         }
396
397                         *evalTokenBuffer++ = SYMBOL;
398 #if 0
399                         *evalTokenBuffer++ = (TOKEN)symbol;
400 #else
401 /*
402 While this approach works, it's wasteful. It would be better to use something
403 that's already available, like the symbol "order defined" table (which needs to
404 be converted from a linked list into an array).
405 */
406                         *evalTokenBuffer++ = symbolNum;
407                         symbolPtr[symbolNum] = symbol;
408                         symbolNum++;
409 #endif
410
411                         if (symbol->sattr & DEFINED)
412                                 *a_value = symbol->svalue;
413                         else
414                                 *a_value = 0;
415
416 /*
417 All that extra crap that was put into the svalue when doing the equr stuff is
418 thrown away right here. What the hell is it for?
419 */
420                         if (symbol->sattre & EQUATEDREG) 
421                                 *a_value &= 0x1F;
422
423                         *a_attr = (WORD)(symbol->sattr & ~GLOBAL);
424
425                         if ((symbol->sattr & (GLOBAL | DEFINED)) == GLOBAL
426                                 && a_esym != NULL)
427                                 *a_esym = symbol;
428
429                         tok += 2;
430                 }
431                 else
432                 {
433                         // Unknown type here... Alert the user!,
434                         error("undefined RISC register in expression");
435                         // Prevent spurious error reporting...
436                         tok++;
437                         return ERROR;
438                 }
439
440                 *evalTokenBuffer++ = ENDEXPR;
441                 return OK;
442         }
443
444         if (expr0() != OK)
445                 return ERROR;
446
447         *evalTokenBuffer++ = ENDEXPR;
448         return evexpr(otk, a_value, a_attr, a_esym);
449 }
450
451
452 //
453 // Evaluate expression.
454 // If the expression involves only ONE external symbol, the expression is
455 // UNDEFINED, but it's value includes everything but the symbol value, and
456 // `a_esym' is set to the external symbol.
457 //
458 int evexpr(TOKEN * tk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
459 {
460         WORD * sattr;
461         VALUE * sval;
462         WORD attr;
463         SYM * sy;
464         SYM * esym;
465         WORD sym_seg;
466
467         sval = evstk;                                                   // (Empty) initial stack
468         sattr = evattr;
469         esym = NULL;                                                    // No external symbol involved
470         sym_seg = 0;
471
472         while (*tk != ENDEXPR)
473         {
474                 switch ((int)*tk++)
475                 {
476                 case SYMBOL:
477 //printf("evexpr(): SYMBOL\n");
478                         sy = symbolPtr[*tk++];
479                         sy->sattr |= REFERENCED;                // Set "referenced" bit 
480
481                         if (!(sy->sattr & DEFINED))
482                         {
483                                 // Reference to undefined symbol
484                                 if (!(sy->sattr & GLOBAL))
485                                 {
486                                         *a_attr = 0;
487                                         *a_value = 0;
488                                         return OK;
489                                 }
490
491                                 if (esym != NULL)                       // Check for multiple externals
492                                         return error(seg_error);
493
494                                 esym = sy;
495                         }
496
497                         if (sy->sattr & DEFINED)
498                         {
499                                 *++sval = sy->svalue;           // Push symbol's value
500                         }
501                         else
502                         {
503                                 *++sval = 0;                            // 0 for undefined symbols 
504                         }
505
506                         *++sattr = (WORD)(sy->sattr & ~GLOBAL); // Push attribs
507                         sym_seg = (WORD)(sy->sattr & TDB);
508                         break;
509                 case CONST:
510 //printf("evexpr(): CONST = %i\n", *tk);
511                         *++sval = *tk++;                                // Push value
512                         *++sattr = ABS | DEFINED;               // Push simple attribs
513                         break;
514                 case ACONST:
515 //printf("evexpr(): ACONST = %i\n", *tk);
516                         *++sval = *tk++;                                // Push value
517                         *++sattr = (WORD)*tk++;                 // Push attribs
518                         break;
519
520                         // Binary "+" and "-" matrix:
521                         // 
522                         //                ABS    Sect     Other
523                         //     ----------------------------
524                         //   ABS     |  ABS   |  Sect  |  Other |
525                         //   Sect    |  Sect  |  [1]   |  Error |
526                         //   Other   |  Other |  Error |  [1]   |
527                         //      ----------------------------
528                         // 
529                         //   [1] + : Error
530                         //       - : ABS
531                 case '+':
532 //printf("evexpr(): +\n");
533                         --sval;                                                 // Pop value
534                         --sattr;                                                // Pop attrib 
535 //printf("--> N+N: %i + %i = ", *sval, sval[1]);
536                         *sval += sval[1];                               // Compute value
537 //printf("%i\n", *sval);
538
539                         if (!(*sattr & TDB))
540                                 *sattr = sattr[1];
541                         else if (sattr[1] & TDB)
542                                 return error(seg_error);
543
544                         break;
545                 case '-':
546 //printf("evexpr(): -\n");
547                         --sval;                                                 // Pop value
548                         --sattr;                                                // Pop attrib 
549 //printf("--> N-N: %i - %i = ", *sval, sval[1]);
550                         *sval -= sval[1];                               // Compute value
551 //printf("%i\n", *sval);
552
553                         attr = (WORD)(*sattr & TDB);
554 #if 0
555 printf("EVEXPR (-): sym1 = %X, sym2 = %X\n", attr, sattr[1]);
556 #endif
557                         // If symbol1 is ABS, take attributes from symbol2
558                         if (!attr)
559                                 *sattr = sattr[1];
560                         // Otherwise, they're both TDB and so attributes cancel out
561                         else if (sattr[1] & TDB)
562                                 *sattr &= ~TDB;
563
564                         break;
565                 // Unary operators only work on ABS items
566                 case UNMINUS:
567 //printf("evexpr(): UNMINUS\n");
568                         if (*sattr & TDB)
569                                 error(seg_error);
570
571                         *sval = -(int)*sval;
572                         *sattr = ABS | DEFINED;                 // Expr becomes absolute
573                         break;
574                 case '!':
575 //printf("evexpr(): !\n");
576                         if (*sattr & TDB)
577                                 error(seg_error);
578
579                         *sval = !*sval;
580                         *sattr = ABS | DEFINED;                 // Expr becomes absolute
581                         break;
582                 case '~':
583 //printf("evexpr(): ~\n");
584                         if (*sattr & TDB)
585                                 error(seg_error);
586
587                         *sval = ~*sval;
588                         *sattr = ABS | DEFINED;                 // Expr becomes absolute
589                         break;
590                 // Comparison operators must have two values that
591                 // are in the same segment, but that's the only requirement.
592                 case LE:
593 //printf("evexpr(): LE\n");
594                         --sattr;
595                         --sval;
596
597                         if ((*sattr & TDB) != (sattr[1] & TDB))
598                                 error(seg_error);
599
600                         *sattr = ABS | DEFINED;
601                         *sval = *sval <= sval[1];
602                         break;
603                 case GE:
604 //printf("evexpr(): GE\n");
605                         --sattr;
606                         --sval;
607
608                         if ((*sattr & TDB) != (sattr[1] & TDB))
609                                 error(seg_error);
610
611                         *sattr = ABS | DEFINED;
612                         *sval = *sval >= sval[1];
613                         break;
614                 case '>':
615 //printf("evexpr(): >\n");
616                         --sattr;
617                         --sval;
618
619                         if ((*sattr & TDB) != (sattr[1] & TDB))
620                                 error(seg_error);
621
622                         *sattr = ABS | DEFINED;
623                         *sval = *sval > sval[1];
624                         break;
625                 case '<':
626 //printf("evexpr(): <\n");
627                         --sattr;
628                         --sval;
629
630                         if ((*sattr & TDB) != (sattr[1] & TDB))
631                                 error(seg_error);
632
633                         *sattr = ABS | DEFINED;
634                         *sval = *sval < sval[1];
635                         break;
636                 case NE:
637 //printf("evexpr(): NE\n");
638                         --sattr;
639                         --sval;
640
641                         if ((*sattr & TDB) != (sattr[1] & TDB))
642                                 error(seg_error);
643
644                         *sattr = ABS | DEFINED;
645                         *sval = *sval != sval[1];
646                         break;
647                 case '=':
648 //printf("evexpr(): =\n");
649                         --sattr;
650                         --sval;
651
652                         if ((*sattr & TDB) != (sattr[1] & TDB))
653                                 error(seg_error);
654
655                         *sattr = ABS | DEFINED;
656                         *sval = *sval == sval[1];
657                         break;
658                 // All other binary operators must have two ABS items
659                 // to work with.  They all produce an ABS value.
660                 default:
661 //printf("evexpr(): default\n");
662                         // GH - Removed for v1.0.15 as part of the fix for indexed loads.
663                         //if ((*sattr & (TEXT|DATA|BSS)) || (*--sattr & (TEXT|DATA|BSS)))
664                         //error(seg_error);
665                         *sattr = ABS | DEFINED;                 // Expr becomes absolute
666
667                         switch ((int)tk[-1])
668                         {
669                         case '*':
670                                 --sval;
671                                 --sattr;                                        // Pop attrib 
672 //printf("--> NxN: %i x %i = ", *sval, sval[1]);
673                                 *sval *= sval[1];
674 //printf("%i\n", *sval);
675                                 break;
676                         case '/':
677                                 --sval;
678                                 --sattr;                                        // Pop attrib 
679
680                                 if (sval[1] == 0)
681                                         return error("divide by zero");
682
683 //printf("--> N/N: %i / %i = ", sval[0], sval[1]);
684                                 // Compiler is picky here: Without casting these, it discards
685                                 // the sign if dividing a negative # by a positive one,
686                                 // creating a bad result. :-/
687                                 // Probably a side effect of using VALUE intead of ints.
688                                 *sval = (int)sval[0] / (int)sval[1];
689 //printf("%i\n", *sval);
690                                 break;
691                         case '%':
692                                 --sval;
693                                 --sattr;                                        // Pop attrib 
694
695                                 if (sval[1] == 0)
696                                         return error("mod (%) by zero");
697
698                                 *sval %= sval[1];
699                                 break;
700                         case SHL:
701                                 --sval;
702                                 --sattr;                                        // Pop attrib 
703                                 *sval <<= sval[1];
704                                 break;
705                         case SHR:
706                                 --sval;
707                                 --sattr;                                        // Pop attrib 
708                                 *sval >>= sval[1];
709                                 break;
710                         case '&':
711                                 --sval;
712                                 --sattr;                                        // Pop attrib 
713                                 *sval &= sval[1];
714                                 break;
715                         case '^':
716                                 --sval;
717                                 --sattr;                                        // Pop attrib 
718                                 *sval ^= sval[1];
719                                 break;
720                         case '|':
721                                 --sval;
722                                 --sattr;                                        // Pop attrib 
723                                 *sval |= sval[1];
724                                 break;
725                         default:
726                                 interror(5);                            // Bad operator in expression stream
727                         }
728                 }
729         }
730
731         if (esym != NULL)
732                 *sattr &= ~DEFINED;
733
734         if (a_esym != NULL)
735                 *a_esym = esym;
736
737         // sym_seg added in 1.0.16 to solve a problem with forward symbols in
738         // expressions where absolute values also existed. The absolutes were
739         // overiding the symbol segments and not being included :(
740         //*a_attr = *sattr | sym_seg;                                        // Copy value + attrib
741
742         *a_attr = *sattr;                                               // Copy value + attrib
743         *a_value = *sval;
744
745         return OK;
746 }
747