xref: /netbsd-src/sys/external/bsd/acpica/dist/compiler/aslcodegen.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /******************************************************************************
2  *
3  * Module Name: aslcodegen - AML code generation
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2018, Intel Corp.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18  *    substantially similar to the "NO WARRANTY" disclaimer below
19  *    ("Disclaimer") and any redistribution must be conditioned upon
20  *    including a substantially similar Disclaimer requirement for further
21  *    binary redistribution.
22  * 3. Neither the names of the above-listed copyright holders nor the names
23  *    of any contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * Alternatively, this software may be distributed under the terms of the
27  * GNU General Public License ("GPL") version 2 as published by the Free
28  * Software Foundation.
29  *
30  * NO WARRANTY
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGES.
42  */
43 
44 #include "aslcompiler.h"
45 #include "aslcompiler.y.h"
46 #include "amlcode.h"
47 #include "acconvert.h"
48 
49 #define _COMPONENT          ACPI_COMPILER
50         ACPI_MODULE_NAME    ("aslcodegen")
51 
52 /* Local prototypes */
53 
54 static ACPI_STATUS
55 CgAmlWriteWalk (
56     ACPI_PARSE_OBJECT       *Op,
57     UINT32                  Level,
58     void                    *Context);
59 
60 static void
61 CgWriteAmlOpcode (
62     ACPI_PARSE_OBJECT       *Op);
63 
64 static void
65 CgWriteTableHeader (
66     ACPI_PARSE_OBJECT       *Op);
67 
68 static void
69 CgCloseTable (
70     void);
71 
72 static void
73 CgWriteNode (
74     ACPI_PARSE_OBJECT       *Op);
75 
76 
77 /*******************************************************************************
78  *
79  * FUNCTION:    CgGenerateAmlOutput
80  *
81  * PARAMETERS:  None.
82  *
83  * RETURN:      None
84  *
85  * DESCRIPTION: Generate AML code. Currently generates the listing file
86  *              simultaneously.
87  *
88  ******************************************************************************/
89 
90 void
91 CgGenerateAmlOutput (
92     void)
93 {
94 
95     /* Generate the AML output file */
96 
97     FlSeekFile (ASL_FILE_SOURCE_OUTPUT, 0);
98     Gbl_SourceLine = 0;
99     Gbl_NextError = Gbl_ErrorLog;
100 
101     TrWalkParseTree (Gbl_ParseTreeRoot, ASL_WALK_VISIT_DOWNWARD,
102         CgAmlWriteWalk, NULL, NULL);
103 
104     DbgPrint (ASL_TREE_OUTPUT, ASL_PARSE_TREE_HEADER2);
105     CgCloseTable ();
106 }
107 
108 
109 /*******************************************************************************
110  *
111  * FUNCTION:    CgAmlWriteWalk
112  *
113  * PARAMETERS:  ASL_WALK_CALLBACK
114  *
115  * RETURN:      Status
116  *
117  * DESCRIPTION: Parse tree walk to generate the AML code.
118  *
119  ******************************************************************************/
120 
121 static ACPI_STATUS
122 CgAmlWriteWalk (
123     ACPI_PARSE_OBJECT       *Op,
124     UINT32                  Level,
125     void                    *Context)
126 {
127 
128     /* Generate the AML for this node */
129 
130     CgWriteNode (Op);
131 
132     if (!Gbl_DebugFlag)
133     {
134         return (AE_OK);
135     }
136 
137     /* Print header at level 0. Alignment assumes 32-bit pointers */
138 
139     if (!Level)
140     {
141         DbgPrint (ASL_TREE_OUTPUT,
142             "\nFinal parse tree used for AML output:\n");
143         DbgPrint (ASL_TREE_OUTPUT, ASL_PARSE_TREE_HEADER2);
144     }
145 
146     /* Dump ParseOp name and possible value */
147 
148     switch (Op->Asl.ParseOpcode)
149     {
150     case PARSEOP_NAMESEG:
151     case PARSEOP_NAMESTRING:
152     case PARSEOP_METHODCALL:
153     case PARSEOP_STRING_LITERAL:
154 
155         UtDumpStringOp (Op, Level);
156         break;
157 
158     default:
159 
160         UtDumpBasicOp (Op, Level);
161         break;
162     }
163 
164     DbgPrint (ASL_TREE_OUTPUT, ASL_PARSE_TREE_DEBUG2,
165         /* 1  */ (UINT32) Op->Asl.Value.Integer,
166         /* 2  */ Op->Asl.ParseOpcode,
167         /* 3  */ Op->Asl.AmlOpcode,
168         /* 4  */ Op->Asl.AmlOpcodeLength,
169         /* 5  */ Op->Asl.AmlPkgLenBytes,
170         /* 6  */ Op->Asl.AmlLength,
171         /* 7  */ Op->Asl.AmlSubtreeLength,
172         /* 8  */ Op->Asl.Parent ? Op->Asl.Parent->Asl.AmlSubtreeLength : 0,
173         /* 9  */ Op,
174         /* 10 */ Op->Asl.Parent,
175         /* 11 */ Op->Asl.Child,
176         /* 12 */ Op->Asl.Next,
177         /* 13 */ Op->Asl.CompileFlags,
178         /* 14 */ Op->Asl.AcpiBtype,
179         /* 15 */ Op->Asl.FinalAmlLength,
180         /* 16 */ Op->Asl.Column,
181         /* 17 */ Op->Asl.LineNumber,
182         /* 18 */ Op->Asl.EndLine,
183         /* 19 */ Op->Asl.LogicalLineNumber,
184         /* 20 */ Op->Asl.EndLogicalLine);
185 
186     TrPrintOpFlags (Op->Asl.CompileFlags, ASL_TREE_OUTPUT);
187     DbgPrint (ASL_TREE_OUTPUT, "\n");
188     return (AE_OK);
189 }
190 
191 
192 /*******************************************************************************
193  *
194  * FUNCTION:    CgLocalWriteAmlData
195  *
196  * PARAMETERS:  Op              - Current parse op
197  *              Buffer          - Buffer to write
198  *              Length          - Size of data in buffer
199  *
200  * RETURN:      None
201  *
202  * DESCRIPTION: Write a buffer of AML data to the AML output file.
203  *
204  ******************************************************************************/
205 
206 void
207 CgLocalWriteAmlData (
208     ACPI_PARSE_OBJECT       *Op,
209     void                    *Buffer,
210     UINT32                  Length)
211 {
212 
213     /* Write the raw data to the AML file */
214 
215     FlWriteFile (ASL_FILE_AML_OUTPUT, Buffer, Length);
216 
217     /* Update the final AML length for this node (used for listings) */
218 
219     if (Op)
220     {
221         Op->Asl.FinalAmlLength += Length;
222     }
223 }
224 
225 
226 /*******************************************************************************
227  *
228  * FUNCTION:    CgWriteAmlOpcode
229  *
230  * PARAMETERS:  Op            - Parse node with an AML opcode
231  *
232  * RETURN:      None.
233  *
234  * DESCRIPTION: Write the AML opcode corresponding to a parse node.
235  *
236  ******************************************************************************/
237 
238 static void
239 CgWriteAmlOpcode (
240     ACPI_PARSE_OBJECT       *Op)
241 {
242     UINT8                   PkgLenFirstByte;
243     UINT32                  i;
244     union {
245         UINT16                  Opcode;
246         UINT8                   OpcodeBytes[2];
247     } Aml;
248     union {
249         UINT32                  Len;
250         UINT8                   LenBytes[4];
251     } PkgLen;
252 
253 
254     /* We expect some DEFAULT_ARGs, just ignore them */
255 
256     if (Op->Asl.ParseOpcode == PARSEOP_DEFAULT_ARG)
257     {
258         return;
259     }
260 
261     /*
262      * Before printing the bytecode, generate comment byte codes
263      * associated with this node.
264      */
265     if (AcpiGbl_CaptureComments)
266     {
267         CgWriteAmlComment(Op);
268     }
269 
270     switch (Op->Asl.AmlOpcode)
271     {
272     case AML_UNASSIGNED_OPCODE:
273 
274         /* These opcodes should not get here */
275 
276         printf ("Found a node with an unassigned AML opcode\n");
277         FlPrintFile (ASL_FILE_STDERR,
278             "Found a node with an unassigned AML opcode\n");
279         return;
280 
281     case AML_INT_RESERVEDFIELD_OP:
282 
283         /* Special opcodes for within a field definition */
284 
285         Aml.Opcode = AML_FIELD_OFFSET_OP;
286         break;
287 
288     case AML_INT_ACCESSFIELD_OP:
289 
290         Aml.Opcode = AML_FIELD_ACCESS_OP;
291         break;
292 
293     case AML_INT_CONNECTION_OP:
294 
295         Aml.Opcode = AML_FIELD_CONNECTION_OP;
296         break;
297 
298     default:
299 
300         Aml.Opcode = Op->Asl.AmlOpcode;
301         break;
302     }
303 
304 
305     switch (Aml.Opcode)
306     {
307     case AML_PACKAGE_LENGTH:
308 
309         /* Value is the length to be encoded (Used in field definitions) */
310 
311         PkgLen.Len = (UINT32) Op->Asl.Value.Integer;
312         break;
313 
314     default:
315 
316         /* Check for two-byte opcode */
317 
318         if (Aml.Opcode > 0x00FF)
319         {
320             /* Write the high byte first */
321 
322             CgLocalWriteAmlData (Op, &Aml.OpcodeBytes[1], 1);
323         }
324 
325         CgLocalWriteAmlData (Op, &Aml.OpcodeBytes[0], 1);
326 
327         /* Subtreelength doesn't include length of package length bytes */
328 
329         PkgLen.Len = Op->Asl.AmlSubtreeLength + Op->Asl.AmlPkgLenBytes;
330         break;
331     }
332 
333     /* Does this opcode have an associated "PackageLength" field? */
334 
335     if (Op->Asl.CompileFlags & OP_AML_PACKAGE)
336     {
337         if (Op->Asl.AmlPkgLenBytes == 1)
338         {
339             /* Simplest case -- no bytes to follow, just write the count */
340 
341             CgLocalWriteAmlData (Op, &PkgLen.LenBytes[0], 1);
342         }
343         else if (Op->Asl.AmlPkgLenBytes != 0)
344         {
345             /*
346              * Encode the "bytes to follow" in the first byte, top two bits.
347              * The low-order nybble of the length is in the bottom 4 bits
348              */
349             PkgLenFirstByte = (UINT8)
350                 (((UINT32) (Op->Asl.AmlPkgLenBytes - 1) << 6) |
351                 (PkgLen.LenBytes[0] & 0x0F));
352 
353             CgLocalWriteAmlData (Op, &PkgLenFirstByte, 1);
354 
355             /*
356              * Shift the length over by the 4 bits we just stuffed
357              * in the first byte
358              */
359             PkgLen.Len >>= 4;
360 
361             /*
362              * Now we can write the remaining bytes -
363              * either 1, 2, or 3 bytes
364              */
365             for (i = 0; i < (UINT32) (Op->Asl.AmlPkgLenBytes - 1); i++)
366             {
367                 CgLocalWriteAmlData (Op, &PkgLen.LenBytes[i], 1);
368             }
369         }
370     }
371 
372     switch (Aml.Opcode)
373     {
374     case AML_BYTE_OP:
375 
376         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 1);
377         break;
378 
379     case AML_WORD_OP:
380 
381         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 2);
382        break;
383 
384     case AML_DWORD_OP:
385 
386         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 4);
387         break;
388 
389     case AML_QWORD_OP:
390 
391         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, 8);
392         break;
393 
394     case AML_STRING_OP:
395 
396         CgLocalWriteAmlData (Op, Op->Asl.Value.String, Op->Asl.AmlLength);
397         break;
398 
399     default:
400 
401         /* All data opcodes must appear above */
402 
403         break;
404     }
405 }
406 
407 
408 /*******************************************************************************
409  *
410  * FUNCTION:    CgWriteTableHeader
411  *
412  * PARAMETERS:  Op        - The DEFINITIONBLOCK node
413  *
414  * RETURN:      None
415  *
416  * DESCRIPTION: Write a table header corresponding to the DEFINITIONBLOCK
417  *
418  ******************************************************************************/
419 
420 static void
421 CgWriteTableHeader (
422     ACPI_PARSE_OBJECT       *Op)
423 {
424     ACPI_PARSE_OBJECT       *Child;
425     UINT32                  CommentLength;
426     ACPI_COMMENT_NODE       *Current;
427 
428 
429     /* AML filename */
430 
431     Child = Op->Asl.Child;
432 
433     /* Signature */
434 
435     Child = Child->Asl.Next;
436 
437     /*
438      * For ASL-/ASL+ converter: replace the table signature with
439      * "XXXX" and save the original table signature. This results in an AML
440      * file with the signature "XXXX". The converter should remove this AML
441      * file. In the event where this AML file does not get deleted, the
442      * "XXXX" table signature prevents this AML file from running on the AML
443      * interpreter.
444      */
445     if (AcpiGbl_CaptureComments)
446     {
447         strncpy(AcpiGbl_TableSig, Child->Asl.Value.String, ACPI_NAME_SIZE);
448         Child->Asl.Value.String = ACPI_SIG_XXXX;
449     }
450 
451     strncpy (TableHeader.Signature, Child->Asl.Value.String, ACPI_NAME_SIZE);
452 
453     /* Revision */
454 
455     Child = Child->Asl.Next;
456     TableHeader.Revision = (UINT8) Child->Asl.Value.Integer;
457 
458     /* Command-line Revision override */
459 
460     if (Gbl_RevisionOverride)
461     {
462         TableHeader.Revision = Gbl_RevisionOverride;
463     }
464 
465     /* OEMID */
466 
467     Child = Child->Asl.Next;
468     strncpy (TableHeader.OemId, Child->Asl.Value.String, ACPI_OEM_ID_SIZE);
469 
470     /* OEM TableID */
471 
472     Child = Child->Asl.Next;
473     strncpy (TableHeader.OemTableId, Child->Asl.Value.String, ACPI_OEM_TABLE_ID_SIZE);
474 
475     /* OEM Revision */
476 
477     Child = Child->Asl.Next;
478     TableHeader.OemRevision = (UINT32) Child->Asl.Value.Integer;
479 
480     /* Compiler ID */
481 
482     ACPI_MOVE_NAME (TableHeader.AslCompilerId, ASL_CREATOR_ID);
483 
484     /* Compiler version */
485 
486     TableHeader.AslCompilerRevision = ACPI_CA_VERSION;
487 
488     /* Table length. Checksum zero for now, will rewrite later */
489 
490     TableHeader.Length = sizeof (ACPI_TABLE_HEADER) +
491         Op->Asl.AmlSubtreeLength;
492 
493     /* Calculate the comment lengths for this definition block parseOp */
494 
495     if (AcpiGbl_CaptureComments)
496     {
497         CvDbgPrint ("Calculating comment lengths for %s in write header\n",
498             Op->Asl.ParseOpName);
499 
500         /*
501          * Take the filename without extensions, add 3 for the new extension
502          * and another 3 for the a908 bytecode and null terminator.
503          */
504         TableHeader.Length += strrchr (Gbl_ParseTreeRoot->Asl.Filename, '.')
505             - Gbl_ParseTreeRoot->Asl.Filename + 1 + 3 + 3;
506         Op->Asl.AmlSubtreeLength +=
507             strlen (Gbl_ParseTreeRoot->Asl.Filename) + 3;
508     CvDbgPrint ("     Length: %lu\n",
509             strlen (Gbl_ParseTreeRoot->Asl.Filename) + 3);
510 
511         if (Op->Asl.CommentList)
512         {
513             Current = Op->Asl.CommentList;
514             while (Current)
515             {
516                 CommentLength = strlen (Current->Comment)+3;
517                 CvDbgPrint ("Length of standard comment): %d\n", CommentLength);
518                 CvDbgPrint ("    Comment string: %s\n\n", Current->Comment);
519                 TableHeader.Length += CommentLength;
520                 Op->Asl.AmlSubtreeLength += CommentLength;
521                 Current = Current->Next;
522             CvDbgPrint ("    Length: %u\n", CommentLength);
523             }
524         }
525         if (Op->Asl.CloseBraceComment)
526         {
527             CommentLength = strlen (Op->Asl.CloseBraceComment)+3;
528             CvDbgPrint ("Length of inline comment +3: %d\n", CommentLength);
529             CvDbgPrint ("    Comment string: %s\n\n", Op->Asl.CloseBraceComment);
530             TableHeader.Length += CommentLength;
531             Op->Asl.AmlSubtreeLength += CommentLength;
532         CvDbgPrint ("    Length: %u\n", CommentLength);
533         }
534     }
535 
536     TableHeader.Checksum = 0;
537 
538     Op->Asl.FinalAmlOffset = ftell (Gbl_Files[ASL_FILE_AML_OUTPUT].Handle);
539 
540     /* Write entire header and clear the table header global */
541 
542     CgLocalWriteAmlData (Op, &TableHeader, sizeof (ACPI_TABLE_HEADER));
543     memset (&TableHeader, 0, sizeof (ACPI_TABLE_HEADER));
544 }
545 
546 
547 /*******************************************************************************
548  *
549  * FUNCTION:    CgUpdateHeader
550  *
551  * PARAMETERS:  Op                  - Op for the Definition Block
552  *
553  * RETURN:      None.
554  *
555  * DESCRIPTION: Complete the ACPI table by calculating the checksum and
556  *              re-writing the header for the input definition block
557  *
558  ******************************************************************************/
559 
560 static void
561 CgUpdateHeader (
562     ACPI_PARSE_OBJECT       *Op)
563 {
564     signed char             Sum;
565     UINT32                  i;
566     UINT32                  Length;
567     UINT8                   FileByte;
568     UINT8                   Checksum;
569 
570 
571     /* Calculate the checksum over the entire definition block */
572 
573     Sum = 0;
574     Length = sizeof (ACPI_TABLE_HEADER) + Op->Asl.AmlSubtreeLength;
575     FlSeekFile (ASL_FILE_AML_OUTPUT, Op->Asl.FinalAmlOffset);
576 
577     for (i = 0; i < Length; i++)
578     {
579         if (FlReadFile (ASL_FILE_AML_OUTPUT, &FileByte, 1) != AE_OK)
580         {
581             printf ("EOF while reading checksum bytes\n");
582             return;
583         }
584 
585         Sum = (signed char) (Sum + FileByte);
586     }
587 
588     Checksum = (UINT8) (0 - Sum);
589 
590     /* Re-write the the checksum byte */
591 
592     FlSeekFile (ASL_FILE_AML_OUTPUT, Op->Asl.FinalAmlOffset +
593         ACPI_OFFSET (ACPI_TABLE_HEADER, Checksum));
594 
595     FlWriteFile (ASL_FILE_AML_OUTPUT, &Checksum, 1);
596 }
597 
598 
599 /*******************************************************************************
600  *
601  * FUNCTION:    CgCloseTable
602  *
603  * PARAMETERS:  None.
604  *
605  * RETURN:      None.
606  *
607  * DESCRIPTION: Complete the ACPI table by calculating the checksum and
608  *              re-writing each table header. This allows support for
609  *              multiple definition blocks in a single source file.
610  *
611  ******************************************************************************/
612 
613 static void
614 CgCloseTable (
615     void)
616 {
617     ACPI_PARSE_OBJECT   *Op;
618 
619 
620     /* Process all definition blocks */
621 
622     Op = Gbl_ParseTreeRoot->Asl.Child;
623     while (Op)
624     {
625         CgUpdateHeader (Op);
626         Op = Op->Asl.Next;
627     }
628 }
629 
630 
631 /*******************************************************************************
632  *
633  * FUNCTION:    CgWriteNode
634  *
635  * PARAMETERS:  Op            - Parse node to write.
636  *
637  * RETURN:      None.
638  *
639  * DESCRIPTION: Write the AML that corresponds to a parse node.
640  *
641  ******************************************************************************/
642 
643 static void
644 CgWriteNode (
645     ACPI_PARSE_OBJECT       *Op)
646 {
647     ASL_RESOURCE_NODE       *Rnode;
648 
649 
650     /* Write all comments here. */
651 
652     if (AcpiGbl_CaptureComments)
653     {
654         CgWriteAmlComment(Op);
655     }
656 
657     /* Always check for DEFAULT_ARG and other "Noop" nodes */
658     /* TBD: this may not be the best place for this check */
659 
660     if ((Op->Asl.ParseOpcode == PARSEOP_DEFAULT_ARG)  ||
661         (Op->Asl.ParseOpcode == PARSEOP_INCLUDE)      ||
662         (Op->Asl.ParseOpcode == PARSEOP_INCLUDE_END))
663     {
664         return;
665     }
666 
667     if ((Op->Asl.ParseOpcode == PARSEOP_EXTERNAL) &&
668         Gbl_DoExternals == FALSE)
669     {
670         return;
671     }
672 
673     Op->Asl.FinalAmlLength = 0;
674 
675     switch (Op->Asl.AmlOpcode)
676     {
677     case AML_RAW_DATA_BYTE:
678     case AML_RAW_DATA_WORD:
679     case AML_RAW_DATA_DWORD:
680     case AML_RAW_DATA_QWORD:
681 
682         CgLocalWriteAmlData (Op, &Op->Asl.Value.Integer, Op->Asl.AmlLength);
683         return;
684 
685 
686     case AML_RAW_DATA_BUFFER:
687 
688         CgLocalWriteAmlData (Op, Op->Asl.Value.Buffer, Op->Asl.AmlLength);
689         return;
690 
691 
692     case AML_RAW_DATA_CHAIN:
693 
694         Rnode = ACPI_CAST_PTR (ASL_RESOURCE_NODE, Op->Asl.Value.Buffer);
695         while (Rnode)
696         {
697             CgLocalWriteAmlData (Op, Rnode->Buffer, Rnode->BufferLength);
698             Rnode = Rnode->Next;
699         }
700         return;
701 
702     default:
703 
704         /* Internal data opcodes must all appear above */
705 
706         break;
707     }
708 
709     switch (Op->Asl.ParseOpcode)
710     {
711     case PARSEOP_DEFAULT_ARG:
712 
713         break;
714 
715     case PARSEOP_DEFINITION_BLOCK:
716 
717         CgWriteTableHeader (Op);
718         if (AcpiGbl_CaptureComments)
719         {
720             CgWriteAmlDefBlockComment (Op);
721         }
722         break;
723 
724     case PARSEOP_NAMESEG:
725     case PARSEOP_NAMESTRING:
726     case PARSEOP_METHODCALL:
727 
728         CgLocalWriteAmlData (Op, Op->Asl.Value.String, Op->Asl.AmlLength);
729         break;
730 
731     default:
732 
733         CgWriteAmlOpcode (Op);
734         break;
735     }
736 }
737