The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/bhnd/tools/nvram_map_gen.awk

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 #!/usr/bin/awk -f
    2 
    3 #-
    4 # Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
    5 # All rights reserved.
    6 #
    7 # Redistribution and use in source and binary forms, with or without
    8 # modification, are permitted provided that the following conditions
    9 # are met:
   10 # 1. Redistributions of source code must retain the above copyright
   11 #    notice, this list of conditions and the following disclaimer,
   12 #    without modification.
   13 # 2. Redistributions in binary form must reproduce at minimum a disclaimer
   14 #    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
   15 #    redistribution must be conditioned upon including a substantially
   16 #    similar Disclaimer requirement for further binary redistribution.
   17 #
   18 # NO WARRANTY
   19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   20 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   21 # LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
   22 # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   23 # THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
   24 # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   25 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   26 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   27 # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   28 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   29 # THE POSSIBILITY OF SUCH DAMAGES.
   30 # 
   31 # $FreeBSD$
   32 
   33 BEGIN   { main() }
   34 END     { at_exit() }
   35 
   36 #
   37 # Print usage
   38 #
   39 function usage() {
   40         print "usage: bhnd_nvram_map.awk <input map> [-hd] [-o output file]"
   41         _EARLY_EXIT = 1
   42         exit 1
   43 }
   44 
   45 function main(_i) {
   46         RS="\n"
   47 
   48         OUTPUT_FILE = null
   49 
   50         # Probe awk implementation's hex digit handling
   51         if ("0xA" + 0 != 10) {
   52                 AWK_REQ_HEX_PARSING=1
   53         }
   54 
   55         # Output type
   56         OUT_T = null
   57         OUT_T_HEADER = "HEADER"
   58         OUT_T_DATA = "DATA"
   59         VERBOSE = 0
   60 
   61         # Tab width to use when calculating output alignment
   62         TAB_WIDTH = 8
   63 
   64         # Enable debug output
   65         DEBUG = 0
   66 
   67         # Maximum revision
   68         REV_MAX = 256
   69 
   70         # Parse arguments
   71         if (ARGC < 2)
   72                 usage()
   73 
   74         for (_i = 1; _i < ARGC; _i++) {
   75                 if (ARGV[_i] == "--debug") {
   76                         DEBUG = 1
   77                 } else if (ARGV[_i] == "-d" && OUT_T == null) {
   78                         OUT_T = OUT_T_DATA
   79                 } else if (ARGV[_i] == "-h" && OUT_T == null) {
   80                         OUT_T = OUT_T_HEADER
   81                 } else if (ARGV[_i] == "-v") {
   82                         VERBOSE = 1
   83                 } else if (ARGV[_i] == "-o") {
   84                         _i++
   85                         if (_i >= ARGC)
   86                                 usage()
   87 
   88                         OUTPUT_FILE = ARGV[_i]
   89                 } else if (ARGV[_i] == "--") {
   90                         _i++
   91                         break
   92                 } else if (ARGV[_i] !~ /^-/) {
   93                         FILENAME = ARGV[_i]
   94                 } else {
   95                         print "unknown option " ARGV[_i]
   96                         usage()
   97                 }
   98         }
   99 
  100         ARGC=2
  101 
  102         if (OUT_T == null) {
  103                 print("error: one of -d or -h required")
  104                 usage()
  105         }
  106 
  107         if (FILENAME == null) {
  108                 print("error: no input file specified")
  109                 usage()
  110         }
  111 
  112         if (OUTPUT_FILE == "-") {
  113                 OUTPUT_FILE = "/dev/stdout"
  114         } else if (OUTPUT_FILE == null) {
  115                 OUTPUT_FILE_IDX = split(FILENAME, _g_output_path, "/")
  116                 OUTPUT_FILE = _g_output_path[OUTPUT_FILE_IDX]
  117 
  118                 if (OUTPUT_FILE !~ /^bhnd_/)
  119                         OUTPUT_FILE = "bhnd_" OUTPUT_FILE
  120 
  121                 if (OUT_T == OUT_T_HEADER)
  122                         OUTPUT_FILE = OUTPUT_FILE ".h" 
  123                 else
  124                         OUTPUT_FILE = OUTPUT_FILE "_data.h"
  125         }
  126 
  127         # Common Regexs
  128         UINT_REGEX      = "^(0|[1-9][0-9]*)$"
  129         HEX_REGEX       = "^(0x[A-Fa-f0-9]+)$"
  130         OFF_REGEX       = "^(0|[1-9][0-9]*)|^(0x[A-Fa-f0-9]+)"
  131         REL_OFF_REGEX   = "^\\+(0|[1-9][0-9]*)|^\\+(0x[A-Fa-f0-9]+)"
  132 
  133         ARRAY_REGEX     = "\\[(0|[1-9][0-9]*)\\]"
  134         TYPES_REGEX     = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?$"
  135 
  136         IDENT_REGEX             = "[A-Za-z_][A-Za-z0-9_]*"
  137         SVAR_IDENT_REGEX        = "^<"IDENT_REGEX">{?$" # <var> identifiers
  138         VAR_IDENT_REGEX         = "^"IDENT_REGEX"{?$"   # var identifiers
  139 
  140         VACCESS_REGEX   = "^(private|internal)$"
  141 
  142         # Property array keys
  143         PROP_ID         = "p_id"
  144         PROP_NAME       = "p_name"
  145 
  146         # Prop path array keys
  147         PPATH_HEAD      = "ppath_head"
  148         PPATH_TAIL      = "ppath_tail"
  149 
  150         # Object array keys
  151         OBJ_IS_CLS      = "o_is_cls"
  152         OBJ_SUPER       = "o_super"
  153         OBJ_PROP        = "o_prop"
  154 
  155         # Class array keys
  156         CLS_NAME        = "cls_name"
  157         CLS_PROP        = "cls_prop"
  158 
  159         # C SPROM binding opcodes/opcode flags
  160         SPROM_OPCODE_EOF                = "SPROM_OPCODE_EOF"
  161         SPROM_OPCODE_NELEM              = "SPROM_OPCODE_NELEM"
  162         SPROM_OPCODE_VAR_END            = "SPROM_OPCODE_VAR_END"
  163         SPROM_OPCODE_VAR_IMM            = "SPROM_OPCODE_VAR_IMM"
  164         SPROM_OPCODE_VAR_REL_IMM        = "SPROM_OPCODE_VAR_REL_IMM"
  165         SPROM_OPCODE_VAR                = "SPROM_OPCODE_VAR"
  166         SPROM_OPCODE_REV_IMM            = "SPROM_OPCODE_REV_IMM"
  167         SPROM_OPCODE_REV_RANGE          = "SPROM_OPCODE_REV_RANGE"
  168           SPROM_OP_REV_START_MASK       = "SPROM_OP_REV_START_MASK"
  169           SPROM_OP_REV_START_SHIFT      = "SPROM_OP_REV_START_SHIFT"
  170           SPROM_OP_REV_END_MASK         = "SPROM_OP_REV_END_MASK"
  171           SPROM_OP_REV_END_SHIFT        = "SPROM_OP_REV_END_SHIFT"
  172         SPROM_OPCODE_MASK_IMM           = "SPROM_OPCODE_MASK_IMM"
  173         SPROM_OPCODE_MASK               = "SPROM_OPCODE_MASK"
  174         SPROM_OPCODE_SHIFT_IMM          = "SPROM_OPCODE_SHIFT_IMM"
  175         SPROM_OPCODE_SHIFT              = "SPROM_OPCODE_SHIFT"
  176         SPROM_OPCODE_OFFSET_REL_IMM     = "SPROM_OPCODE_OFFSET_REL_IMM"
  177         SPROM_OPCODE_OFFSET             = "SPROM_OPCODE_OFFSET"
  178         SPROM_OPCODE_TYPE               = "SPROM_OPCODE_TYPE"
  179         SPROM_OPCODE_TYPE_IMM           = "SPROM_OPCODE_TYPE_IMM"
  180         SPROM_OPCODE_DO_BINDN_IMM       = "SPROM_OPCODE_DO_BINDN_IMM"
  181         SPROM_OPCODE_DO_BIND            = "SPROM_OPCODE_DO_BIND"
  182         SPROM_OPCODE_DO_BINDN           = "SPROM_OPCODE_DO_BINDN"
  183           SPROM_OP_BIND_SKIP_IN_MASK    = "SPROM_OP_BIND_SKIP_IN_MASK"
  184           SPROM_OP_BIND_SKIP_IN_SHIFT   = "SPROM_OP_BIND_SKIP_IN_SHIFT"
  185           SPROM_OP_BIND_SKIP_IN_SIGN    = "SPROM_OP_BIND_SKIP_IN_SIGN"
  186           SPROM_OP_BIND_SKIP_OUT_MASK   = "SPROM_OP_BIND_SKIP_OUT_MASK"
  187           SPROM_OP_BIND_SKIP_OUT_SHIFT  = "SPROM_OP_BIND_SKIP_OUT_SHIFT"
  188 
  189         SPROM_OP_DATA_U8                = "SPROM_OP_DATA_U8"
  190         SPROM_OP_DATA_U8_SCALED         = "SPROM_OP_DATA_U8_SCALED"
  191         SPROM_OP_DATA_U16               = "SPROM_OP_DATA_U16"
  192         SPROM_OP_DATA_U32               = "SPROM_OP_DATA_U32"
  193         SPROM_OP_DATA_I8                = "SPROM_OP_DATA_I8"
  194 
  195         SPROM_OP_BIND_SKIP_IN_MAX       =  3    # maximum SKIP_IN value
  196         SPROM_OP_BIND_SKIP_IN_MIN       = -3    # minimum SKIP_IN value
  197         SPROM_OP_BIND_SKIP_OUT_MAX      =  1    # maximum SKIP_OUT value
  198         SPROM_OP_BIND_SKIP_OUT_MIN      =  0    # minimum SKIP_OUT value
  199         SPROM_OP_IMM_MAX                = 15    # maximum immediate value
  200         SPROM_OP_REV_RANGE_MAX          = 15    # maximum SROM rev range value
  201 
  202         # SPROM opcode encoding state
  203         SromOpStream = class_new("SromOpStream")
  204                 class_add_prop(SromOpStream, p_layout, "layout")
  205                 class_add_prop(SromOpStream, p_revisions, "revisions")
  206                 class_add_prop(SromOpStream, p_vid, "vid")
  207                 class_add_prop(SromOpStream, p_offset, "offset")
  208                 class_add_prop(SromOpStream, p_type, "type")
  209                 class_add_prop(SromOpStream, p_nelem, "nelem")
  210                 class_add_prop(SromOpStream, p_mask, "mask")
  211                 class_add_prop(SromOpStream, p_shift, "shift")
  212                 class_add_prop(SromOpStream, p_bind_total, "bind_total")
  213                 class_add_prop(SromOpStream, p_pending_bind, "pending_bind")
  214 
  215         # SROM pending bind operation
  216         SromOpBind = class_new("SromOpBind")
  217                 class_add_prop(SromOpBind, p_segment, "segment")
  218                 class_add_prop(SromOpBind, p_count, "count")
  219                 class_add_prop(SromOpBind, p_offset, "offset")
  220                 class_add_prop(SromOpBind, p_width, "width")
  221                 class_add_prop(SromOpBind, p_skip_in, "skip_in")
  222                 class_add_prop(SromOpBind, p_skip_out, "skip_out")
  223                 class_add_prop(SromOpBind, p_buffer, "buffer")
  224 
  225         # Map class definition
  226         Map = class_new("Map")
  227 
  228         # Array class definition
  229         Array = class_new("Array")
  230                 class_add_prop(Array, p_count, "count")
  231 
  232         # MacroType class definition
  233         # Used to define a set of known macro types that may be generated
  234         MacroType = class_new("MacroType")
  235                 class_add_prop(MacroType, p_name, "name")
  236                 class_add_prop(MacroType, p_const_suffix, "const_suffix")
  237 
  238         MTypeVarName    = macro_type_new("name", "")            # var name
  239         MTypeVarID      = macro_type_new("id", "_ID")           # var unique ID
  240         MTypeVarMaxLen  = macro_type_new("len", "_MAXLEN")      # var max array length
  241 
  242         # Preprocessor Constant
  243         MacroDefine = class_new("MacroDefine")
  244                 class_add_prop(MacroDefine, p_name, "name")
  245                 class_add_prop(MacroDefine, p_value, "value")
  246 
  247         # ParseState definition
  248         ParseState = class_new("ParseState")
  249                 class_add_prop(ParseState, p_ctx, "ctx")
  250                 class_add_prop(ParseState, p_is_block, "is_block")
  251                 class_add_prop(ParseState, p_line, "line")
  252 
  253         # Value Formats
  254         Fmt = class_new("Fmt")
  255                 class_add_prop(Fmt, p_name, "name")
  256                 class_add_prop(Fmt, p_symbol, "symbol")
  257                 class_add_prop(Fmt, p_array_fmt, "array_fmt")
  258 
  259         FmtHex          = fmt_new("hex", "bhnd_nvram_val_bcm_hex_fmt")
  260         FmtDec          = fmt_new("decimal", "bhnd_nvram_val_bcm_decimal_fmt")
  261         FmtMAC          = fmt_new("macaddr", "bhnd_nvram_val_bcm_macaddr_fmt")
  262         FmtLEDDC        = fmt_new("leddc", "bhnd_nvram_val_bcm_leddc_fmt")
  263         FmtCharArray    = fmt_new("char_array", "bhnd_nvram_val_char_array_fmt")
  264         FmtChar         = fmt_new("char", "bhnd_nvram_val_char_array_fmt",
  265                               FmtCharArray)
  266         FmtStr          = fmt_new("string", "bhnd_nvram_val_bcm_string_fmt")
  267 
  268         # User-specifiable value formats
  269         ValueFormats = map_new()
  270                 map_set(ValueFormats, get(FmtHex,       p_name), FmtHex)
  271                 map_set(ValueFormats, get(FmtDec,       p_name), FmtDec)
  272                 map_set(ValueFormats, get(FmtMAC,       p_name), FmtMAC)
  273                 map_set(ValueFormats, get(FmtLEDDC,     p_name), FmtLEDDC)
  274                 map_set(ValueFormats, get(FmtStr,       p_name), FmtStr)
  275 
  276         # Data Types
  277         Type = class_new("Type")
  278                 class_add_prop(Type, p_name, "name")
  279                 class_add_prop(Type, p_width, "width")
  280                 class_add_prop(Type, p_signed, "signed")
  281                 class_add_prop(Type, p_const, "const")
  282                 class_add_prop(Type, p_const_val, "const_val")
  283                 class_add_prop(Type, p_array_const, "array_const")
  284                 class_add_prop(Type, p_array_const_val, "array_const_val")
  285                 class_add_prop(Type, p_default_fmt, "default_fmt")
  286                 class_add_prop(Type, p_mask, "mask")
  287 
  288         ArrayType = class_new("ArrayType", AST)
  289                 class_add_prop(ArrayType, p_type, "type")
  290                 class_add_prop(ArrayType, p_count, "count")
  291 
  292         UInt8Max        =  255
  293         UInt16Max       =  65535
  294         UInt32Max       =  4294967295
  295         Int8Min         = -128
  296         Int8Max         =  127
  297         Int16Min        = -32768
  298         Int16Max        =  32767
  299         Int32Min        = -2147483648
  300         Int32Max        =  2147483648
  301         CharMin         =  Int8Min
  302         CharMax         =  Int8Max
  303 
  304         UInt8   = type_new("u8", 1, 0, "BHND_NVRAM_TYPE_UINT8",
  305            "BHND_NVRAM_TYPE_UINT8_ARRAY", FmtHex, UInt8Max, 0, 16)
  306 
  307         UInt16  = type_new("u16", 2, 0, "BHND_NVRAM_TYPE_UINT16",
  308            "BHND_NVRAM_TYPE_UINT16_ARRAY", FmtHex, UInt16Max, 1, 17)
  309 
  310         UInt32  = type_new("u32", 4, 0, "BHND_NVRAM_TYPE_UINT32",
  311            "BHND_NVRAM_TYPE_UINT32_ARRAY", FmtHex, UInt32Max, 2, 18)
  312 
  313         Int8    = type_new("i8", 1, 1, "BHND_NVRAM_TYPE_INT8",
  314            "BHND_NVRAM_TYPE_INT8_ARRAY", FmtDec, UInt8Max, 4, 20)
  315 
  316         Int16   = type_new("i16", 2, 1, "BHND_NVRAM_TYPE_INT16",
  317            "BHND_NVRAM_TYPE_INT16_ARRAY", FmtDec, UInt16Max, 5, 21)
  318 
  319         Int32   = type_new("i32", 4, 1, "BHND_NVRAM_TYPE_INT32",
  320            "BHND_NVRAM_TYPE_INT32_ARRAY", FmtDec, UInt32Max, 6, 22)
  321 
  322         Char    = type_new("char", 1, 1, "BHND_NVRAM_TYPE_CHAR",
  323            "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtChar, UInt8Max, 8, 24)
  324 
  325         BaseTypes = map_new()
  326                 map_set(BaseTypes, get(UInt8,   p_name), UInt8)
  327                 map_set(BaseTypes, get(UInt16,  p_name), UInt16)
  328                 map_set(BaseTypes, get(UInt32,  p_name), UInt32)
  329                 map_set(BaseTypes, get(Int8,    p_name), Int8)
  330                 map_set(BaseTypes, get(Int16,   p_name), Int16)
  331                 map_set(BaseTypes, get(Int32,   p_name), Int32)
  332                 map_set(BaseTypes, get(Char,    p_name), Char)
  333 
  334         BaseTypesArray = map_to_array(BaseTypes)
  335         BaseTypesCount = array_size(BaseTypesArray)
  336 
  337         # Variable Flags
  338         VFlag = class_new("VFlag")
  339                 class_add_prop(VFlag, p_name, "name")
  340                 class_add_prop(VFlag, p_const, "const")
  341 
  342         VFlagPrivate    = vflag_new("private", "BHND_NVRAM_VF_MFGINT")
  343         VFlagIgnoreAll1 = vflag_new("ignall1", "BHND_NVRAM_VF_IGNALL1")
  344 
  345         # Variable Access Type Constants
  346         VAccess = class_new("VAccess")
  347         VAccessPublic   = obj_new(VAccess)      # Public
  348         VAccessPrivate  = obj_new(VAccess)      # MFG Private
  349         VAccessInternal = obj_new(VAccess)      # Implementation-Internal
  350 
  351         #
  352         # AST node classes
  353         #
  354         AST = class_new("AST")
  355                 class_add_prop(AST, p_line, "line")
  356 
  357         SymbolContext = class_new("SymbolContext", AST)
  358                 class_add_prop(SymbolContext, p_vars, "vars")
  359 
  360         # NVRAM root parser context
  361         NVRAM = class_new("NVRAM", SymbolContext)
  362                 class_add_prop(NVRAM, p_var_groups, "var_groups")
  363                 class_add_prop(NVRAM, p_srom_layouts, "srom_layouts")
  364                 class_add_prop(NVRAM, p_srom_table, "srom_table")
  365 
  366         # Variable Group
  367         VarGroup = class_new("VarGroup", SymbolContext)
  368                 class_add_prop(VarGroup, p_name, "name")
  369 
  370         # Revision Range
  371         RevRange = class_new("RevRange", AST)
  372                 class_add_prop(RevRange, p_start, "start")
  373                 class_add_prop(RevRange, p_end, "end")
  374 
  375         # String Constant
  376         StringConstant = class_new("StringConstant", AST)
  377                 class_add_prop(StringConstant, p_value, "value")                # string
  378                 class_add_prop(StringConstant, p_continued, "continued")        # bool
  379 
  380         # Variable Declaration
  381         Var = class_new("Var", AST)
  382                 class_add_prop(Var, p_access, "access")         # VAccess
  383                 class_add_prop(Var, p_name, "name")             # string
  384                 class_add_prop(Var, p_desc, "desc")             # StringConstant
  385                 class_add_prop(Var, p_help, "help")             # StringConstant
  386                 class_add_prop(Var, p_type, "type")             # AbstractType
  387                 class_add_prop(Var, p_fmt, "fmt")               # Fmt
  388                 class_add_prop(Var, p_ignall1, "ignall1")       # bool
  389                 # ID is assigned once all variables are sorted
  390                 class_add_prop(Var, p_vid, "vid")               # int
  391 
  392         # Common interface inherited by parser contexts that support
  393         # registration of SROM variable entries
  394         SromContext = class_new("SromContext", AST)
  395                 class_add_prop(SromContext, p_revisions, "revisions")
  396 
  397         # SROM Layout Node
  398         SromLayout = class_new("SromLayout", SromContext)
  399                 class_add_prop(SromLayout, p_entries, "entries")        # Array<SromEntry>
  400                 class_add_prop(SromLayout, p_revmap, "revmap")          # Map<(string,int), SromEntry>
  401                 class_add_prop(SromLayout, p_output_var_counts,         # Map<int, int> (rev->count)
  402                     "output_var_counts")
  403 
  404         # SROM Layout Filter Node
  405         # Represents a filter over a parent SromLayout's revisions 
  406         SromLayoutFilter = class_new("SromLayoutFilter", SromContext)
  407                 class_add_prop(SromLayoutFilter, p_parent, "parent")
  408 
  409         # SROM variable entry
  410         SromEntry = class_new("SromEntry", AST)
  411                 class_add_prop(SromEntry, p_var, "var")
  412                 class_add_prop(SromEntry, p_revisions, "revisions")
  413                 class_add_prop(SromEntry, p_base_offset, "base_offset")
  414                 class_add_prop(SromEntry, p_type, "type")
  415                 class_add_prop(SromEntry, p_offsets, "offsets")
  416 
  417         # SROM variable offset
  418         SromOffset = class_new("SromOffset", AST)
  419                 class_add_prop(SromOffset, p_segments, "segments")
  420 
  421         # SROM variable offset segment
  422         SromSegment = class_new("SromSegment", AST)
  423                 class_add_prop(SromSegment, p_offset, "offset")
  424                 class_add_prop(SromSegment, p_type, "type")
  425                 class_add_prop(SromSegment, p_mask, "mask")
  426                 class_add_prop(SromSegment, p_shift, "shift")
  427                 class_add_prop(SromSegment, p_value, "value")
  428 
  429         # Create the parse state stack
  430         _g_parse_stack_depth = 0
  431         _g_parse_stack[0] = null
  432 
  433         # Push the root parse state
  434         parser_state_push(nvram_new(), 0)
  435 }
  436 
  437 function at_exit(_block_start, _state, _output_vars, _noutput_vars, _name, _var,
  438     _i)
  439 {
  440         # Skip completion handling if exiting from an error
  441         if (_EARLY_EXIT)
  442                 exit 1
  443 
  444         # Check for complete block closure
  445         if (!in_parser_context(NVRAM)) {
  446                 _state = parser_state_get()
  447                 _block_start = get(_state, p_line)
  448                 errorx("missing '}' for block opened on line " _block_start "")
  449         }
  450 
  451         # Apply lexicographical sorting to our variable names. To support more
  452         # effecient table searching, we guarantee a stable sort order (using C
  453         # collation).
  454         #
  455         # This also has a side-effect of generating a unique monotonic ID
  456         # for all variables, which we will emit as a #define and can use as a
  457         # direct index into the C variable table
  458         _output_vars = array_new()
  459         for (_name in _g_var_names) {
  460                 _var = _g_var_names[_name]
  461 
  462                 # Don't include internal variables in the output
  463                 if (var_is_internal(_var))
  464                         continue
  465 
  466                 array_append(_output_vars, _var)
  467         }
  468 
  469         # Sort by variable name
  470         array_sort(_output_vars, prop_to_path(p_name))
  471 
  472         # Set all variable ID properties to their newly assigned ID value
  473         _noutput_vars = array_size(_output_vars)
  474         for (_i = 0; _i < _noutput_vars; _i++) {
  475                 _var = array_get(_output_vars, _i)
  476                 set(_var, p_vid, _i)
  477         }
  478 
  479         # Truncate output file and write common header
  480         printf("") > OUTPUT_FILE
  481         emit("/*\n")
  482         emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n")
  483         emit(" *\n")
  484         emit(" * generated from nvram map: " FILENAME "\n")
  485         emit(" */\n")
  486         emit("\n")
  487 
  488         # Emit all variable definitions
  489         if (OUT_T == OUT_T_DATA) {
  490                 write_data(_output_vars)
  491         } else if (OUT_T == OUT_T_HEADER) {
  492                 write_header(_output_vars)
  493         }
  494         if (VERBOSE == 1) {
  495                 printf("%u variable records written to %s\n", array_size(_output_vars),
  496                        OUTPUT_FILE) >> "/dev/stderr"
  497         }
  498 }
  499 
  500 # Write the public header (output type HEADER)
  501 function write_header(output_vars, _noutput_vars, _var,
  502     _tab_align, _macro, _macros, _num_macros, _i)
  503 {
  504         # Produce our array of #defines
  505         _num_macros = 0
  506         _noutput_vars = array_size(output_vars)
  507         for (_i = 0; _i < _noutput_vars; _i++) {
  508                 _var = array_get(output_vars, _i)
  509 
  510                 # Variable name
  511                 _macro = var_get_macro(_var, MTypeVarName, \
  512                     "\"" get(_var, p_name) "\"")
  513                 _macros[_num_macros++] = _macro
  514 
  515                 # Variable array length
  516                 if (var_has_array_type(_var)) {                 
  517                         _macro = var_get_macro(_var, MTypeVarMaxLen,
  518                             var_get_array_len(_var))
  519                         _macros[_num_macros++] = _macro
  520                 }
  521         }
  522 
  523         # Calculate value tab alignment position for our macros
  524         _tab_align = macros_get_tab_alignment(_macros, _num_macros)
  525 
  526         # Write the macros
  527         for (_i = 0; _i < _num_macros; _i++)
  528                 write_macro_define(_macros[_i], _tab_align)
  529 }
  530 
  531 # Write the private data header (output type DATA)
  532 function write_data(output_vars, _noutput_vars, _var, _nvram, _layouts,
  533     _nlayouts, _layout, _revs, _rev, _rev_start, _rev_end, _base_type,
  534     _srom_table, _nsrom_table, _i, _j)
  535 {
  536         _nvram = parser_state_get_context(NVRAM)
  537         _layouts = get(_nvram, p_srom_layouts)
  538         _nlayouts = array_size(_layouts)
  539 
  540         _noutput_vars = array_size(output_vars)
  541 
  542         # Write all our private NVAR_ID defines
  543         write_data_defines(output_vars)
  544 
  545         # Write all layout binding opcodes, and build an array
  546         # mapping SROM revision to corresponding SROM layout
  547         _srom_table = array_new()
  548         for (_i = 0; _i < _nlayouts; _i++) {
  549                 _layout = array_get(_layouts, _i)
  550 
  551                 # Write binding opcode table to our output file
  552                 write_srom_bindings(_layout)
  553 
  554                 # Add entries to _srom_table for all covered revisions
  555                 _revs = get(_layout, p_revisions)
  556                 _rev_start = get(_revs, p_start)
  557                 _rev_end = get(_revs, p_end)
  558 
  559                 for (_j = _rev_start; _j <= _rev_end; _j++)
  560                         array_append(_srom_table, _j)
  561         }
  562 
  563         # Sort in ascending order, by SROM revision
  564         array_sort(_srom_table)
  565         _nsrom_table = array_size(_srom_table)
  566 
  567         # Write the variable definitions
  568         emit("/* Variable definitions */\n")
  569         emit("const struct bhnd_nvram_vardefn " \
  570             "bhnd_nvram_vardefns[] = {\n")
  571         output_depth++
  572         for (_i = 0; _i < _noutput_vars; _i++) {
  573                 write_data_nvram_vardefn(array_get(output_vars, _i))
  574         }
  575         output_depth--
  576         emit("};\n")
  577         emit("const size_t bhnd_nvram_num_vardefns = " _noutput_vars ";\n")
  578 
  579         # Write static asserts for raw type constant values that must be kept
  580         # synchronized with the code
  581         for (_i = 0; _i < BaseTypesCount; _i++) {
  582                 _base_type = array_get(BaseTypesArray, _i)
  583 
  584                 emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
  585                     type_get_const(_base_type), type_get_const_val(_base_type),
  586                     "type constant out of sync"))
  587 
  588                 emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
  589                     get(_base_type, p_array_const),
  590                     get(_base_type, p_array_const_val),
  591                     "array type constant out of sync"))
  592         }
  593 
  594         # Write all top-level bhnd_sprom_layout entries
  595         emit("/* SPROM layouts */\n")
  596         emit("const struct bhnd_sprom_layout bhnd_sprom_layouts[] = {\n")
  597         output_depth++
  598         for (_i = 0; _i < _nsrom_table; _i++) {
  599                 _rev = array_get(_srom_table, _i)
  600                 _layout = nvram_get_srom_layout(_nvram, _rev)
  601                 write_data_srom_layout(_layout, _rev)
  602         }
  603         output_depth--
  604         emit("};\n")
  605         emit("const size_t bhnd_sprom_num_layouts = " _nsrom_table ";\n")
  606 }
  607 
  608 # Write a bhnd_nvram_vardef entry for the given variable
  609 function write_data_nvram_vardefn(v, _desc, _help, _type, _fmt) {
  610         obj_assert_class(v, Var)
  611 
  612         _desc = get(v, p_desc)
  613         _help = get(v, p_help)
  614         _type = get(v, p_type)
  615         _fmt = var_get_fmt(v)
  616 
  617         emit("{\n")
  618         output_depth++
  619         emit(sprintf(".name = \"%s\",\n", get(v, p_name)))
  620 
  621         if (_desc != null)
  622                 emit(sprintf(".desc = \"%s\",\n", get(_desc, p_value)))
  623         else
  624                 emit(".desc = NULL,\n")
  625 
  626         if (_help != null)
  627                 emit(sprintf(".help = \"%s\",\n", get(_help, p_value)))
  628         else
  629                 emit(".help = NULL,\n")
  630 
  631         emit(".type = " type_get_const(_type) ",\n")
  632         emit(".nelem = " var_get_array_len(v) ",\n")
  633         emit(".fmt = &" get(_fmt, p_symbol) ",\n")
  634         emit(".flags = " gen_var_flags(v) ",\n")
  635 
  636         output_depth--
  637         emit("},\n")
  638 }
  639 
  640 # Write a top-level bhnd_sprom_layout entry for the given revision
  641 # and layout definition
  642 function write_data_srom_layout(layout, revision, _flags, _size,
  643     _sromcrc, _crc_seg, _crc_off,
  644     _sromsig, _sig_seg, _sig_offset, _sig_value,
  645     _sromrev, _rev_seg, _rev_off,
  646     _num_vars)
  647 {
  648         _flags = array_new()
  649 
  650         # Calculate the size; it always follows the internal CRC variable
  651         _sromcrc = srom_layout_find_entry(layout, "<sromcrc>", revision)
  652         if (_sromcrc == null) {
  653                 errorx("missing '<sromcrc>' entry for '"revision"' layout, " \
  654                     "cannot compute total size")
  655         } else {
  656                 _crc_seg = srom_entry_get_single_segment(_sromcrc)
  657                 _crc_off = get(_crc_seg, p_offset)
  658                 _size = _crc_off
  659                 _size += get(get(_crc_seg, p_type), p_width)
  660         }
  661 
  662         # Fetch signature definition
  663         _sromsig = srom_layout_find_entry(layout, "<sromsig>", revision)
  664         if (_sromsig == null) {
  665                 array_append(_flags, "SPROM_LAYOUT_MAGIC_NONE")
  666         } else {
  667                 _sig_seg = srom_entry_get_single_segment(_sromsig)
  668 
  669                 _sig_offset = get(_sig_seg, p_offset)
  670                 _sig_value = get(_sig_seg, p_value)
  671                 if (_sig_value == "")
  672                         errorc(get(_sromsig, p_line), "missing signature value")
  673         }
  674 
  675         # Fetch sromrev definition
  676         _sromrev = srom_layout_find_entry(layout, "sromrev", revision)
  677         if (_sromrev == null) {
  678                 errorx("missing 'sromrev' entry for '"revision"' layout, " \
  679                     "cannot determine offset")
  680         } else {
  681                 # Must be a u8 value
  682                 if (!type_equal(get(_sromrev, p_type), UInt8)) {
  683                         errorx("'sromrev' entry has non-u8 type '" \
  684                             type_to_string(get(_sromrev, p_type)))
  685                 }
  686 
  687                 _rev_seg = srom_entry_get_single_segment(_sromrev)
  688                 _rev_off = get(_rev_seg, p_offset)
  689         }
  690 
  691         # Write layout entry
  692         emit("{\n")
  693         output_depth++
  694         emit(".size = "_size",\n")
  695         emit(".rev = "revision",\n")
  696 
  697         if (array_size(_flags) > 0) {
  698                 emit(".flags = " array_join(_flags, "|") ",\n")
  699         } else {
  700                 emit(".flags = 0,\n")
  701         }
  702 
  703         emit(".srev_offset = " _rev_off ",\n")
  704 
  705         if (_sromsig != null) {
  706                 emit(".magic_offset = " _sig_offset ",\n")
  707                 emit(".magic_value = " _sig_value ",\n")
  708         } else {
  709                 emit(".magic_offset = 0,\n")
  710                 emit(".magic_value = 0,\n")
  711         }
  712 
  713         emit(".crc_offset = " _crc_off ",\n")
  714 
  715         emit(".bindings = " srom_layout_get_variable_name(layout) ",\n")
  716         emit(".bindings_size = nitems(" \
  717             srom_layout_get_variable_name(layout) "),\n")
  718 
  719         emit(".num_vars = " srom_layout_num_output_vars(layout, revision) ",\n")
  720 
  721         obj_delete(_flags)
  722 
  723         output_depth--
  724         emit("},\n");
  725 }
  726 
  727 # Create a new opstream encoding state instance for the given layout
  728 function srom_ops_new(layout, _obj) {
  729         obj_assert_class(layout, SromLayout)
  730 
  731         _obj = obj_new(SromOpStream)
  732         set(_obj, p_layout, layout)
  733         set(_obj, p_revisions, get(layout, p_revisions))
  734         set(_obj, p_vid, 0)
  735         set(_obj, p_offset, 0)
  736         set(_obj, p_type, null)
  737         set(_obj, p_mask, null)
  738         set(_obj, p_shift, null)
  739 
  740         return (_obj)
  741 }
  742 
  743 # Return the current type width, or throw an error if no type is currently
  744 # specified.
  745 function srom_ops_get_type_width(opstream, _type)
  746 {
  747         obj_assert_class(opstream, SromOpStream)
  748 
  749         _type = get(opstream, p_type)
  750         if (_type == null)
  751                 errorx("no type value set")
  752 
  753         return (get(type_get_base(_type), p_width))
  754 }
  755 
  756 # Write a string to the SROM opcode stream, either buffering the write,
  757 # or emitting it directly.
  758 function srom_ops_emit(opstream, string, _pending_bind, _buffer) {
  759         obj_assert_class(opstream, SromOpStream)
  760 
  761         # Buffered?
  762         if ((_pending_bind = get(opstream, p_pending_bind)) != null) {
  763                 _buffer = get(_pending_bind, p_buffer)
  764                 array_append(_buffer, string)
  765                 return
  766         }
  767 
  768         # Emit directly
  769         emit(string)
  770 }
  771 
  772 # Emit a SROM opcode followed by up to four optional bytes
  773 function srom_ops_emit_opcode(opstream, opcode, arg0, arg1, arg2, arg3) {
  774         obj_assert_class(opstream, SromOpStream)
  775 
  776         srom_ops_emit(opstream, opcode",\n")
  777         if (arg0 != "") srom_ops_emit(opstream, arg0",\n")
  778         if (arg1 != "") srom_ops_emit(opstream, arg1",\n")
  779         if (arg2 != "") srom_ops_emit(opstream, arg2",\n")
  780         if (arg3 != "") srom_ops_emit(opstream, arg3",\n")
  781 }
  782 
  783 # Emit a SROM opcode and associated integer value, choosing the best
  784 # SROM_OP_DATA variant for encoding the value.
  785 #
  786 # opc:          The standard opcode for non-IMM encoded data, or null if none
  787 # opc_imm:      The IMM opcode, or null if none
  788 # value:        The value to encode
  789 # svalue:       Symbolic representation of value to include in output, or null
  790 function srom_ops_emit_int_opcode(opstream, opc, opc_imm, value, svalue,
  791     _width, _offset, _delta)
  792 {
  793         obj_assert_class(opstream, SromOpStream)
  794 
  795         # Fetch current type width
  796         _width = srom_ops_get_type_width(opstream)
  797 
  798         # Special cases:
  799         if (opc_imm == SPROM_OPCODE_SHIFT_IMM) {
  800                 # SHIFT_IMM -- the imm value must be positive and divisible by
  801                 # two (shift/2) to use the IMM form.
  802                 if (value >= 0 && value % 2 == 0) {
  803                         value = (value/2)
  804                         opc = null
  805                 } else {
  806                         opc_imm = null
  807                 }
  808         } else if (opc_imm == SPROM_OPCODE_OFFSET_REL_IMM) {
  809                 # OFFSET_REL_IMM -- the imm value must be positive, divisible
  810                 # by the type width, and relative to the last offset to use
  811                 # the IMM form.
  812 
  813                 # Assert that callers correctly flushed any pending bind before
  814                 # attempting to set a relative offset
  815                 if (get(opstream, p_pending_bind) != null)
  816                         errorx("can't set relative offset with a pending bind")
  817 
  818                 # Fetch current offset, calculate relative value and determine
  819                 # whether we can issue an IMM opcode
  820                 _offset = get(opstream, p_offset)
  821                 _delta = value - _offset
  822                 if (_delta >= 0 &&
  823                     _delta % _width == 0 &&
  824                     (_delta/_width) <= SPROM_OP_IMM_MAX)
  825                 {
  826                         srom_ops_emit(opstream,
  827                             sprintf("/* %#x + %#x -> %#x */\n", _offset,
  828                             _delta, value))
  829                         value = (_delta / _width)
  830                         opc = null
  831                 } else {
  832                         opc_imm = null
  833                 }
  834         }
  835 
  836         # If no symbolic representation provided, write the raw value
  837         if (svalue == null)
  838                 svalue = value
  839 
  840         # Try to encode as IMM value?
  841         if (opc_imm != null && value >= 0 && value <= SPROM_OP_IMM_MAX) {
  842                 srom_ops_emit_opcode(opstream, "("opc_imm"|"svalue")")
  843                 return
  844         }
  845 
  846         # Can't encode as immediate; do we have a non-immediate form?
  847         if (opc == null)
  848                 errorx("can't encode '" value "' as immediate, and no " \
  849                     "non-immediate form was provided")
  850 
  851         # Determine and emit minimal encoding
  852         # We let the C compiler perform the bit operations, rather than
  853         # trying to wrestle awk's floating point arithmetic
  854         if (value < 0) {
  855                 # Only Int8 is used
  856                  if (value < Int8Min)
  857                          errorx("cannot int8 encode '" value "'")
  858 
  859                 srom_ops_emit_opcode(opstream,
  860                     "("opc"|"SPROM_OP_DATA_I8")", svalue)
  861 
  862         } else if (value <= UInt8Max) {
  863 
  864                 srom_ops_emit_opcode(opstream,
  865                     "("opc"|"SPROM_OP_DATA_U8")", svalue)
  866 
  867         } else if (value % _width == 0 && (value / _width) <= UInt8Max) {
  868 
  869                 srom_ops_emit_opcode(opstream,
  870                     "("opc"|"SPROM_OP_DATA_U8_SCALED")", svalue / _width)
  871 
  872         } else if (value <= UInt16Max) {
  873 
  874                 srom_ops_emit_opcode(opstream,
  875                     "("opc"|"SPROM_OP_DATA_U16")", 
  876                     "("svalue" & 0xFF)",
  877                     "("svalue" >> 8)")
  878 
  879         } else if (value <= UInt32Max) {
  880 
  881                 srom_ops_emit_opcode(opstream,
  882                     "("opc"|"SPROM_OP_DATA_U32")", 
  883                     "("svalue" & 0xFF)",
  884                     "(("svalue" >> 8) & 0xFF)",
  885                     "(("svalue" >> 16) & 0xFF)",
  886                     "(("svalue" >> 24) & 0xFF)")
  887 
  888         } else {
  889                 errorx("can't encode '" value "' (too large)")
  890         }
  891 }
  892 
  893 # Emit initial OPCODE_VAR opcode and update opstream state
  894 function srom_ops_reset_var(opstream, var, _vid_prev, _vid, _vid_name,
  895     _type, _base_type)
  896 {
  897         obj_assert_class(opstream, SromOpStream)
  898         obj_assert_class(var, Var)
  899 
  900         # Flush any pending bind for the previous variable
  901         srom_ops_flush_bind(opstream, 1)
  902 
  903         # Fetch current state
  904         _vid_prev = get(opstream, p_vid)
  905 
  906         _vid = get(var, p_vid)
  907         _vid_name = var_get_macro_name(var, MTypeVarID)
  908 
  909         # Update state
  910         _type = get(var, p_type)
  911         set(opstream, p_vid, _vid)
  912         set(opstream, p_type, type_get_base(_type))
  913         set(opstream, p_nelem, var_get_array_len(var))
  914         set(opstream, p_mask, type_get_default_mask(_type))
  915         set(opstream, p_shift, 0)
  916         set(opstream, p_bind_total, 0)
  917 
  918         # Always provide a human readable comment
  919         srom_ops_emit(opstream, sprintf("/* %s (%#x) */\n", get(var, p_name),
  920             get(opstream, p_offset)))
  921 
  922         # Prefer a single VAR_IMM byte
  923         if (_vid_prev == 0 || _vid <= SPROM_OP_IMM_MAX) {
  924                 srom_ops_emit_int_opcode(opstream,
  925                     null, SPROM_OPCODE_VAR_IMM,
  926                     _vid, _vid_name)
  927                 return
  928         }
  929 
  930         # Try encoding as a single VAR_REL_IMM byte
  931         if (_vid_prev <= _vid && (_vid - _vid_prev) <= SPROM_OP_IMM_MAX) {
  932                 srom_ops_emit_int_opcode(opstream,
  933                     null, SPROM_OPCODE_VAR_REL_IMM,
  934                     _vid - _vid_prev, null)
  935                 return
  936         }
  937 
  938         # Fall back on a multibyte encoding
  939         srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_VAR, null, _vid,
  940             _vid_name)
  941 }
  942 
  943 # Emit OPCODE_REV/OPCODE_REV_RANGE (if necessary) for a new revision range
  944 function srom_ops_emit_revisions(opstream, revisions, _prev_revs,
  945     _start, _end)
  946 {
  947         obj_assert_class(opstream, SromOpStream)
  948         _prev_revs = get(opstream, p_revisions)
  949 
  950         if (revrange_equal(_prev_revs, revisions))
  951                 return;
  952 
  953         # Update stream state
  954         set(opstream, p_revisions, revisions)
  955 
  956         _start = get(revisions, p_start)
  957         _end = get(revisions, p_end)
  958 
  959         # Sanity-check range values
  960         if (_start < 0 || _end < 0)
  961                 errorx("invalid range: " revrange_to_string(revisions))
  962 
  963         # If range covers a single revision, and can be encoded within
  964         # SROM_OP_IMM_MAX, we can use the single byte encoding
  965         if (_start == _end && _start <= SPROM_OP_IMM_MAX) {
  966                 srom_ops_emit_int_opcode(opstream,
  967                     null, SPROM_OPCODE_REV_IMM, _start)
  968                 return
  969         }
  970 
  971         # Otherwise, we need to use the two byte range encoding
  972         if (_start > SPROM_OP_REV_RANGE_MAX || _end > SPROM_OP_REV_RANGE_MAX) {
  973                 errorx(sprintf("cannot encode range values %s (>= %u)",
  974                     revrange_to_string(revisions), SPROM_OP_REV_RANGE_MAX))
  975         }
  976 
  977         srom_ops_emit_opcode(opstream,
  978             SPROM_OPCODE_REV_RANGE,
  979             sprintf("(%u << %s) | (%u << %s)",
  980                 _start, SPROM_OP_REV_START_SHIFT,
  981                 _end, SPROM_OP_REV_END_SHIFT))
  982 }
  983 
  984 # Emit OPCODE_OFFSET (if necessary) for a new offset
  985 function srom_ops_emit_offset(opstream, offset, _prev_offset, _rel_offset,
  986     _bind)
  987 {
  988         obj_assert_class(opstream, SromOpStream)
  989 
  990         # Flush any pending bind before adjusting the offset
  991         srom_ops_flush_bind(opstream, 0)
  992 
  993         # Fetch current offset
  994         _prev_offset = get(opstream, p_offset)
  995         if (_prev_offset == offset)
  996                 return
  997 
  998         # Encode (possibly a relative, 1-byte form) of the offset opcode
  999         srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_OFFSET,
 1000             SPROM_OPCODE_OFFSET_REL_IMM, offset, null)
 1001 
 1002         # Update state
 1003         set(opstream, p_offset, offset)
 1004 }
 1005 
 1006 # Emit OPCODE_TYPE (if necessary) for a new type value; this also
 1007 # resets the mask to the type default.
 1008 function srom_ops_emit_type(opstream, type, _base_type, _prev_type, _prev_mask,
 1009     _default_mask)
 1010 {
 1011         obj_assert_class(opstream, SromOpStream)
 1012         if (!obj_is_instanceof(type, ArrayType))
 1013                 obj_assert_class(type, Type)
 1014 
 1015         _default_mask = type_get_default_mask(type)
 1016         _base_type = type_get_base(type)
 1017 
 1018         # If state already matches the requested type, nothing to be
 1019         # done
 1020         _prev_type = get(opstream, p_type)
 1021         _prev_mask = get(opstream, p_mask)
 1022         if (type_equal(_prev_type, _base_type) && _prev_mask == _default_mask)
 1023                 return
 1024 
 1025         # Update state
 1026         set(opstream, p_type, _base_type)
 1027         set(opstream, p_mask, _default_mask)
 1028 
 1029         # Emit opcode.
 1030         if (type_get_const_val(_base_type) <= SPROM_OP_IMM_MAX) {
 1031                 # Single byte IMM encoding
 1032                 srom_ops_emit_opcode(opstream,
 1033                     SPROM_OPCODE_TYPE_IMM "|" type_get_const(_base_type))
 1034         } else {
 1035                 # Two byte encoding
 1036                 srom_ops_emit_opcode(opstream, SPROM_OPCODE_TYPE,
 1037                     type_get_const(_base_type))
 1038         }
 1039 }
 1040 
 1041 # Emit OPCODE_MASK (if necessary) for a new mask value
 1042 function srom_ops_emit_mask(opstream, mask, _prev_mask) {
 1043         obj_assert_class(opstream, SromOpStream)
 1044         _prev_mask = get(opstream, p_mask)
 1045 
 1046         if (_prev_mask == mask)
 1047                 return
 1048 
 1049         set(opstream, p_mask, mask)
 1050         srom_ops_emit_int_opcode(opstream,
 1051             SPROM_OPCODE_MASK, SPROM_OPCODE_MASK_IMM,
 1052             mask, sprintf("0x%x", mask))
 1053 }
 1054 
 1055 # Emit OPCODE_SHIFT (if necessary) for a new shift value
 1056 function srom_ops_emit_shift(opstream, shift, _prev_shift) {
 1057         obj_assert_class(opstream, SromOpStream)
 1058         _prev_shift = get(opstream, p_shift)
 1059 
 1060         if (_prev_shift == shift)
 1061                 return
 1062 
 1063         set(opstream, p_shift, shift)
 1064         srom_ops_emit_int_opcode(opstream,
 1065             SPROM_OPCODE_SHIFT, SPROM_OPCODE_SHIFT_IMM,
 1066             shift, null)
 1067 }
 1068 
 1069 # Return true if a valid BIND/BINDN encoding exists for the given SKIP_IN
 1070 # value, false if the skip values exceed the limits of the bind opcode
 1071 # family.
 1072 function srom_ops_can_encode_skip_in(skip_in) {
 1073         return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
 1074             skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
 1075 }
 1076 
 1077 # Return true if a valid BIND/BINDN encoding exists for the given SKIP_OUT
 1078 # value, false if the skip values exceed the limits of the bind opcode
 1079 # family.
 1080 function srom_ops_can_encode_skip_out(skip_out) {
 1081         return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
 1082             skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
 1083 }
 1084 
 1085 # Return true if a valid BIND/BINDN encoding exists for the given skip
 1086 # values, false if the skip values exceed the limits of the bind opcode
 1087 # family.
 1088 function srom_ops_can_encode_skip(skip_in, skip_out) {
 1089         return (srom_ops_can_encode_skip_in(skip_in) &&
 1090             srom_ops_can_encode_skip_out(skip_out))
 1091 }
 1092 
 1093 # Create a new SromOpBind instance for the given segment
 1094 function srom_opbind_new(segment, skip_in, skip_out, _obj, _type, _width,
 1095     _offset)
 1096 {
 1097         obj_assert_class(segment, SromSegment)
 1098 
 1099         # Verify that an encoding exists for the skip values
 1100         if (!srom_ops_can_encode_skip_in(skip_in)) {
 1101                 errorx(sprintf("cannot encode SKIP_IN=%d; maximum supported " \
 1102                     "range %d-%d", skip_in,
 1103                     SPROM_OP_BIND_SKIP_IN_MIN, SPROM_OP_BIND_SKIP_IN_MAX))
 1104         }
 1105 
 1106         if (!srom_ops_can_encode_skip_out(skip_out)) {
 1107                 errorx(sprintf("cannot encode SKIP_OUT=%d; maximum supported " \
 1108                     "range %d-%d", skip_out,
 1109                     SPROM_OP_BIND_SKIP_OUT_MIN, SPROM_OP_BIND_SKIP_OUT_MAX))
 1110         }
 1111 
 1112         # Fetch basic segment info
 1113         _offset = get(segment, p_offset)
 1114         _type = srom_segment_get_base_type(segment)
 1115         _width = get(_type, p_width)
 1116 
 1117         # Construct new instance
 1118         _obj = obj_new(SromOpBind)
 1119 
 1120         set(_obj, p_segment, segment)
 1121         set(_obj, p_count, 1)
 1122         set(_obj, p_offset, _offset)
 1123         set(_obj, p_width, _width)
 1124         set(_obj, p_skip_in, skip_in)
 1125         set(_obj, p_skip_out, skip_out)
 1126         set(_obj, p_buffer, array_new())
 1127 
 1128         return (_obj)
 1129 }
 1130 
 1131 # Try to coalesce a BIND for the given segment with an existing bind request,
 1132 # returning true on success, or false if the two segments cannot be coalesced
 1133 # into the existing request
 1134 function srom_opbind_append(bind, segment, skip_out, _bind_seg, _bind_off,
 1135     _width, _count, _skip_in, _seg_offset, _delta)
 1136 {
 1137         obj_assert_class(bind, SromOpBind)
 1138         obj_assert_class(segment, SromSegment)
 1139 
 1140         # Are the segments compatible?
 1141         _bind_seg = get(bind, p_segment)
 1142         if (!srom_segment_attributes_equal(_bind_seg, segment))
 1143                 return (0)
 1144 
 1145         # Are the output skip values compatible?
 1146         if (get(bind, p_skip_out) != skip_out)
 1147                 return (0)
 1148 
 1149         # Find bind offset/count/width/skip
 1150         _bind_off = get(bind, p_offset)
 1151         _count = get(bind, p_count)
 1152         _skip_in = get(bind, p_skip_in)
 1153         _width = get(bind, p_width)
 1154 
 1155         # Fetch new segment's offset
 1156         _seg_offset = get(segment, p_offset)
 1157 
 1158         # If there's only one segment in the bind op, we ned to compute the
 1159         # skip value to be used for all later segments (including the
 1160         # segment we're attempting to append)
 1161         #
 1162         # If there's already multiple segments, we just need to verify that
 1163         # the bind_offset + (count * width * skip_in) produces the new
 1164         # segment's offset
 1165         if (_count == 1) {
 1166                 # Determine the delta between the two segment offsets. This
 1167                 # must be a multiple of the type width to be encoded
 1168                 # as a BINDN entry
 1169                 _delta = _seg_offset - _bind_off
 1170                 if ((_delta % _width) != 0)
 1171                         return (0)
 1172 
 1173                 # The skip byte count is calculated as (type width * skip)
 1174                 _skip_in = _delta / _width
 1175 
 1176                 # Is the skip encodable?
 1177                 if (!srom_ops_can_encode_skip_in(_skip_in))
 1178                         return (0)
 1179 
 1180                 # Save required skip
 1181                 set(bind, p_skip_in, _skip_in)
 1182         } else if (_count > 1) {
 1183                 # Get the final offset of the binding if we were to add
 1184                 # one additional segment
 1185                 _bind_off = _bind_off + (_width * _skip_in * (_count + 1))
 1186 
 1187                 # If it doesn't match our segment's offset, we can't
 1188                 # append this segment
 1189                 if (_bind_off != _seg_offset)
 1190                         return (0)
 1191         }
 1192 
 1193         # Success! Increment the bind count in the existing bind
 1194         set(bind, p_count, _count + 1)
 1195         return (1)
 1196 }
 1197 
 1198 # Return true if the given binding operation can be omitted from the output
 1199 # if it would be immediately followed by a VAR, VAR_REL_IMM, or EOF opcode.
 1200 #
 1201 # The bind operatin must be configured with default count, skip_in, and 
 1202 # skip_out values of 1, and must contain no buffered post-BIND opcodes 
 1203 function srom_opbind_is_implicit_encodable(bind) {
 1204         obj_assert_class(bind, SromOpBind)
 1205 
 1206         if (get(bind, p_count) != 1)
 1207                 return (0)
 1208 
 1209         if (get(bind, p_skip_in) != 1)
 1210                 return (0)
 1211 
 1212         if (get(bind, p_skip_out) != 1)
 1213                 return (0)
 1214 
 1215         if (array_size(get(bind, p_buffer)) != 0)
 1216                 return (0)
 1217 
 1218         return (1)
 1219 }
 1220 
 1221 
 1222 # Encode all segment settings for a single offset segment, followed by a bind
 1223 # request.
 1224 #
 1225 # opstream:     Opcode stream
 1226 # segment:      Segment to be written
 1227 # continued:    If this segment's value should be OR'd with the value of a
 1228 #               following segment
 1229 function srom_ops_emit_segment(opstream, segment, continued, _value,
 1230     _bind, _skip_in, _skip_out)
 1231 {
 1232         obj_assert_class(opstream, SromOpStream)
 1233         obj_assert_class(segment, SromSegment)
 1234 
 1235         # Determine basic bind parameters
 1236         _count = 1
 1237         _skip_in = 1
 1238         _skip_out = continued ? 0 : 1
 1239 
 1240         # Try to coalesce with a pending binding
 1241         if ((_bind = get(opstream, p_pending_bind)) != null) {
 1242                 if (srom_opbind_append(_bind, segment, _skip_out))
 1243                         return
 1244         }
 1245 
 1246         # Otherwise, flush any pending bind and enqueue our own
 1247         srom_ops_flush_bind(opstream, 0)
 1248         if (get(opstream, p_pending_bind))
 1249                 errorx("bind not flushed!")
 1250 
 1251         # Encode type
 1252         _value = get(segment, p_type)
 1253         srom_ops_emit_type(opstream, _value)
 1254 
 1255         # Encode offset
 1256         _value = get(segment, p_offset)
 1257         srom_ops_emit_offset(opstream, _value)
 1258 
 1259         # Encode mask
 1260         _value = get(segment, p_mask)
 1261         srom_ops_emit_mask(opstream, _value)
 1262 
 1263         # Encode shift
 1264         _value = get(segment, p_shift)
 1265         srom_ops_emit_shift(opstream, _value)
 1266 
 1267         # Enqueue binding with opstream
 1268         _bind = srom_opbind_new(segment, _skip_in, _skip_out)
 1269         set(opstream, p_pending_bind, _bind)
 1270 }
 1271 
 1272 # (private) Adjust the stream's input offset by applying the given bind
 1273 # operation's skip_in * width * count.
 1274 function _srom_ops_apply_bind_offset(opstream, bind, _count, _offset, _width,
 1275     _skip_in, _opstream_offset)
 1276 {
 1277         obj_assert_class(opstream, SromOpStream)
 1278         obj_assert_class(bind, SromOpBind)
 1279 
 1280         _opstream_offset = get(opstream, p_offset)
 1281         _offset = get(bind, p_offset)
 1282         if (_opstream_offset != _offset)
 1283                 errorx("stream/bind offset state mismatch")
 1284 
 1285         _count = get(bind, p_count)
 1286         _width = get(bind, p_width)
 1287         _skip_in = get(bind, p_skip_in)
 1288 
 1289         set(opstream, p_offset,
 1290             _opstream_offset + ((_width * _skip_in) * _count))
 1291 }
 1292 
 1293 # (private) Write a bind instance and all buffered opcodes
 1294 function _srom_ops_emit_bind(opstream, bind, _count, _skip_in, _skip_out,
 1295     _off_start, _width, _si_signbit, _written, _nbuffer, _buffer)
 1296 {
 1297         obj_assert_class(opstream, SromOpStream)
 1298         obj_assert_class(bind, SromOpBind)
 1299 
 1300         # Assert that any pending bind state has already been cleared
 1301         if (get(opstream, p_pending_bind) != null)
 1302                 errorx("cannot flush bind with an existing pending_bind active")
 1303 
 1304         # Fetch (and assert valid) our skip values
 1305         _skip_in = get(bind, p_skip_in)
 1306         _skip_out = get(bind, p_skip_out)
 1307 
 1308         if (!srom_ops_can_encode_skip(_skip_in, _skip_out))
 1309                 errorx("invalid skip values in buffered bind")
 1310 
 1311         # Determine SKIP_IN sign bit
 1312         _si_signbit = "0"
 1313         if (_skip_in < 0)
 1314                 _si_signbit = SPROM_OP_BIND_SKIP_IN_SIGN
 1315 
 1316         # Emit BIND/BINDN opcodes until the full count is encoded
 1317         _count = get(bind, p_count)
 1318         while (_count > 0) {
 1319                 if (_count > 1 && _count <= SPROM_OP_IMM_MAX &&
 1320                     _skip_in == 1 && _skip_out == 1)
 1321                 {
 1322                         # The one-byte BINDN form requires encoding the count
 1323                         # as a IMM, and has an implicit in/out skip of 1.
 1324                         srom_ops_emit_opcode(opstream,
 1325                             "("SPROM_OPCODE_DO_BINDN_IMM"|"_count")")
 1326                         _count -= _count
 1327 
 1328                 } else if (_count > 1) {
 1329                         # The two byte BINDN form can encode skip values and a
 1330                         # larger U8 count
 1331                         _written = min(_count, UInt8Max)
 1332 
 1333                         srom_ops_emit_opcode(opstream,
 1334                             sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
 1335                                 SPROM_OPCODE_DO_BINDN,
 1336                                 _si_signbit,
 1337                                 abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
 1338                                 _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT),
 1339                             _written)
 1340                         _count -= _written
 1341 
 1342                 } else {
 1343                         # The 1-byte BIND form can encode the same SKIP values
 1344                         # as the 2-byte BINDN, with a implicit count of 1
 1345                         srom_ops_emit_opcode(opstream,
 1346                             sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
 1347                                 SPROM_OPCODE_DO_BIND,
 1348                                 _si_signbit,
 1349                                 abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
 1350                                 _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT))
 1351                         _count--
 1352                 }
 1353         }
 1354 
 1355         # Update the stream's input offset
 1356         _srom_ops_apply_bind_offset(opstream, bind)
 1357 
 1358         # Write any buffered post-BIND opcodes
 1359         _buffer = get(bind, p_buffer)
 1360         _nbuffer = array_size(_buffer)
 1361         for (_i = 0; _i < _nbuffer; _i++)
 1362                 srom_ops_emit(opstream, array_get(_buffer, _i))
 1363 }
 1364 
 1365 # Flush any buffered binding
 1366 function srom_ops_flush_bind(opstream, allow_implicit, _bind, _bind_total)
 1367 {
 1368         obj_assert_class(opstream, SromOpStream)
 1369 
 1370         # If no pending bind, nothing to flush
 1371         if ((_bind = get(opstream, p_pending_bind)) == null)
 1372                 return
 1373 
 1374         # Check the per-variable bind count to determine whether
 1375         # we can encode an implicit bind.
 1376         #
 1377         # If there have been any explicit bind statements, implicit binding
 1378         # cannot be used.
 1379         _bind_total = get(opstream, p_bind_total)
 1380         if (allow_implicit && _bind_total > 0) {
 1381                 # Disable implicit encoding; explicit bind statements have
 1382                 # been issued for this variable previously.
 1383                 allow_implicit = 0
 1384         }
 1385 
 1386         # Increment bind count
 1387         set(opstream, p_bind_total, _bind_total + 1)
 1388 
 1389         # Clear the property value
 1390         set(opstream, p_pending_bind, null)
 1391 
 1392         # If a pending bind operation can be encoded as an implicit bind,
 1393         # emit a descriptive comment and update the stream state.
 1394         #
 1395         # Otherwise, emit the full set of bind opcode(s)
 1396         _base_off = get(opstream, p_offset)
 1397         if (allow_implicit && srom_opbind_is_implicit_encodable(_bind)) {
 1398                 # Update stream's input offset
 1399                 _srom_ops_apply_bind_offset(opstream, _bind)
 1400         } else {
 1401                 _srom_ops_emit_bind(opstream, _bind)
 1402         }
 1403 
 1404         # Provide bind information as a comment
 1405         srom_ops_emit(opstream,
 1406             sprintf("/* bind (%s @ %#x -> %#x) */\n",
 1407                 type_to_string(get(opstream, p_type)),
 1408                 _base_off, get(opstream, p_offset)))
 1409 
 1410         # Clean up
 1411         obj_delete(_bind)
 1412 }
 1413 
 1414 # Write OPCODE_EOF after flushing any buffered writes
 1415 function srom_ops_emit_eof(opstream) {
 1416         obj_assert_class(opstream, SromOpStream)
 1417 
 1418         # Flush any buffered writes
 1419         srom_ops_flush_bind(opstream, 1)
 1420 
 1421         # Emit an explicit VAR_END opcode for the last entry
 1422         srom_ops_emit_opcode(opstream, SPROM_OPCODE_VAR_END)
 1423 
 1424         # Emit EOF
 1425         srom_ops_emit_opcode(opstream, SPROM_OPCODE_EOF)
 1426 }
 1427 
 1428 # Write the SROM offset segment bindings to the opstream
 1429 function write_srom_offset_bindings(opstream, offsets,
 1430     _noffsets, _offset, _segs, _nsegs, _segment, _cont,
 1431     _i, _j)
 1432 {
 1433         _noffsets = array_size(offsets)
 1434         for (_i = 0; _i < _noffsets; _i++) {
 1435                 # Encode each segment in this offset 
 1436                 _offset = array_get(offsets, _i)
 1437                 _segs = get(_offset, p_segments)
 1438                 _nsegs = array_size(_segs)
 1439 
 1440                 for (_j = 0; _j < _nsegs; _j++) {
 1441                         _segment = array_get(_segs, _j)
 1442                         _cont = 0
 1443                         
 1444                         # Should this value be OR'd with the next segment?
 1445                         if (_j+1 < _nsegs)
 1446                                 _cont = 1 
 1447 
 1448                         # Encode segment
 1449                         srom_ops_emit_segment(opstream, _segment, _cont)
 1450                 }
 1451         }
 1452 }
 1453 
 1454 # Write the SROM entry stream for a SROM entry to the output file
 1455 function write_srom_entry_bindings(entry, opstream, _var, _vid,
 1456     _var_type, _entry_type, _offsets, _noffsets)
 1457 {
 1458         _var = get(entry, p_var)
 1459         _vid = get(_var, p_vid)
 1460 
 1461         # Encode revision switch. This resets variable state, so must
 1462         # occur before any variable definitions to which it applies
 1463         srom_ops_emit_revisions(opstream, get(entry, p_revisions))
 1464 
 1465         # Encode variable ID
 1466         srom_ops_reset_var(opstream, _var, _vid)
 1467         output_depth++
 1468 
 1469         # Write entry-specific array length (SROM layouts may define array
 1470         # mappings with fewer elements than in the variable definition)
 1471         if (srom_entry_has_array_type(entry)) {
 1472                 _var_type = get(_var, p_type)
 1473                 _entry_type = get(entry, p_type)
 1474 
 1475                 # If the array length differs from the variable default,
 1476                 # write an OPCODE_EXT_NELEM entry
 1477                 if (type_get_nelem(_var_type) != type_get_nelem(_entry_type)) {
 1478                         srom_ops_emit_opcode(opstream, SPROM_OPCODE_NELEM,
 1479                             srom_entry_get_array_len(entry))
 1480                 }
 1481         }
 1482 
 1483         # Write offset segment bindings
 1484         _offsets = get(entry, p_offsets)
 1485         write_srom_offset_bindings(opstream, _offsets)
 1486         output_depth--
 1487 }
 1488 
 1489 # Write a SROM layout binding opcode table to the output file
 1490 function write_srom_bindings(layout, _varname, _var, _all_entries,
 1491     _nall_entries, _entries, _nentries, _entry, _opstream, _i)
 1492 {
 1493         _varname = srom_layout_get_variable_name(layout)
 1494         _all_entries = get(layout, p_entries)
 1495         _opstream = srom_ops_new(layout)
 1496 
 1497         #
 1498         # Collect all entries to be included in the output, and then
 1499         # sort by their variable's assigned ID (ascending).
 1500         #
 1501         # The variable IDs were previously assigned in lexigraphical sort
 1502         # order; since the variable *offsets* tend to match this order, this
 1503         # works out well for our compact encoding, allowing us to make use of
 1504         # compact relative encoding of both variable IDs and variable offsets.
 1505         #
 1506         _entries = array_new()
 1507         _nall_entries = array_size(_all_entries)
 1508         for (_i = 0; _i < _nall_entries; _i++) {
 1509                 _entry = array_get(_all_entries, _i)
 1510                 _var = get(_entry, p_var)
 1511 
 1512                 # Skip internal variables
 1513                 if (var_is_internal(_var))
 1514                         continue
 1515 
 1516                 # Sanity check variable ID assignment
 1517                 if (get(_var, p_vid) == "")
 1518                         errorx("missing variable ID for " obj_to_string(_var))
 1519         
 1520                 array_append(_entries, _entry)
 1521         }
 1522 
 1523         # Sort entries by (variable ID, revision range), ascending
 1524         array_sort(_entries, prop_path_create(p_var, p_vid),
 1525             prop_path_create(p_revisions, p_start),
 1526             prop_path_create(p_revisions, p_end))
 1527 
 1528         # Emit all entry binding opcodes
 1529         emit("static const uint8_t " _varname "[] = {\n")
 1530         output_depth++
 1531 
 1532         _nentries = array_size(_entries)
 1533         for (_i = 0; _i < _nentries; _i++) {
 1534                 _entry = array_get(_entries, _i)
 1535                 write_srom_entry_bindings(_entry, _opstream)
 1536         }
 1537 
 1538         # Flush and write EOF
 1539         srom_ops_emit_eof(_opstream)
 1540 
 1541         output_depth--
 1542         emit("};\n")
 1543 
 1544         obj_delete(_opstream)
 1545         obj_delete(_entries)
 1546 }
 1547 
 1548 # Write the BHND_NVAR_<NAME>_ID #defines to the output file
 1549 function write_data_defines(output_vars, _noutput_vars, _tab_align, _var,
 1550     _macro, _macros, _num_macros, _i)
 1551 {
 1552         # Produce our array of #defines
 1553         _num_macros = 0
 1554         _noutput_vars = array_size(output_vars)
 1555         for (_i = 0; _i < _noutput_vars; _i++) {
 1556                 _var = array_get(output_vars, _i)
 1557 
 1558                 # Variable ID
 1559                 _macro = var_get_macro(_var, MTypeVarID, get(_var, p_vid))
 1560                 _macros[_num_macros++] = _macro
 1561         }
 1562 
 1563         # Calculate value tab alignment position for our macros
 1564         _tab_align = macros_get_tab_alignment(_macros, _num_macros)
 1565 
 1566         # Write the #defines
 1567         emit("/* ID constants provide an index into the variable array */\n")
 1568         for (_i = 0; _i < _num_macros; _i++)
 1569                 write_macro_define(_macros[_i], _tab_align)
 1570         emit("\n\n");
 1571 }
 1572 
 1573 # Calculate the common tab alignment to be used with a set of prefix strings
 1574 # with the given maximum length
 1575 function tab_alignment(max_len, _tab_align) {
 1576         _tab_align = max_len
 1577         _tab_align += (TAB_WIDTH - (_tab_align % TAB_WIDTH)) % TAB_WIDTH
 1578         _tab_align /= TAB_WIDTH
 1579 
 1580         return (_tab_align)
 1581 }
 1582 
 1583 # Generate and return a tab string that can be appended to a string of
 1584 # `strlen` to pad the column out to `align_to`
 1585 #
 1586 # Note: If the string from which strlen was derived contains tabs, the result
 1587 # is undefined
 1588 function tab_str(strlen, align_to, _lead, _pad, _result, _i) {
 1589         _lead = strlen
 1590         _lead -= (_lead % TAB_WIDTH);
 1591         _lead /= TAB_WIDTH;
 1592 
 1593         # Determine required padding to reach the desired alignment
 1594         if (align_to >= _lead)
 1595                 _pad = align_to - _lead;
 1596         else
 1597                 _pad = 1;
 1598 
 1599         for (_i = 0; _i < _pad; _i++)
 1600                 _result = _result "\t"
 1601 
 1602         return (_result)
 1603 }
 1604 
 1605 
 1606 # Write a MacroDefine constant, padding the constant out to `align_to`
 1607 function write_macro_define(macro, align_to, _tabstr, _i) {
 1608         # Determine required padding to reach the desired alignment
 1609         _tabstr = tab_str(length(get(macro, p_name)), align_to)
 1610 
 1611         emit("#define\t" get(macro, p_name) _tabstr get(macro, p_value) "\n")
 1612 }
 1613 
 1614 # Calculate the tab alignment to be used with a given integer-indexed array
 1615 # of Macro instances.
 1616 function macros_get_tab_alignment(macros, macros_len, _macro, _max_len, _i) {
 1617         _max_len = 0
 1618         for (_i = 0; _i < macros_len; _i++) {
 1619                 _macro = macros[_i]
 1620                 _max_len = max(_max_len, length(get(_macro, p_name)))
 1621         }
 1622 
 1623         return (tab_alignment(_max_len))
 1624 }
 1625 
 1626 # Variable group block
 1627 $1 == "group" && in_parser_context(NVRAM) {
 1628         parse_variable_group()
 1629 }
 1630 
 1631 # Variable definition
 1632 (($1 ~ VACCESS_REGEX && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) &&
 1633     in_parser_context(SymbolContext) \
 1634 {
 1635         parse_variable_defn()
 1636 }
 1637 
 1638 # Variable "fmt" parameter
 1639 $1 == "fmt" && in_parser_context(Var) {
 1640         parse_variable_param($1)
 1641         next
 1642 }
 1643 
 1644 # Variable "all1" parameter
 1645 $1 == "all1" && in_parser_context(Var) {
 1646         parse_variable_param($1)
 1647         next
 1648 }
 1649 
 1650 # Variable desc/help parameters
 1651 ($1 == "desc" || $1 == "help") && in_parser_context(Var) {
 1652         parse_variable_param($1)
 1653         next
 1654 }
 1655 
 1656 # SROM layout block
 1657 $1 == "srom" && in_parser_context(NVRAM) {
 1658         parse_srom_layout()
 1659 }
 1660 
 1661 
 1662 # SROM layout revision filter block
 1663 $1 == "srom" && in_parser_context(SromLayout) {
 1664         parse_srom_layout_filter()
 1665 }
 1666 
 1667 # SROM layout variable entry
 1668 $1 ~ "("OFF_REGEX"):$" && \
 1669     (in_parser_context(SromLayout) || in_parser_context(SromLayoutFilter)) \
 1670 {
 1671         parse_srom_variable_entry()
 1672 }
 1673 
 1674 
 1675 # SROM entry segment
 1676 $1 ~ "("REL_OFF_REGEX"|"OFF_REGEX")[:,|]?" && in_parser_context(SromEntry) {
 1677         parse_srom_entry_segments()
 1678 }
 1679 
 1680 # Skip comments and blank lines
 1681 /^[ \t]*#/ || /^$/ {
 1682         next
 1683 }
 1684 
 1685 # Close blocks
 1686 /}/ && !in_parser_context(NVRAM) {
 1687         while (!in_parser_context(NVRAM) && $0 ~ "}") {
 1688                 parser_state_close_block();
 1689         }
 1690         next
 1691 }
 1692 
 1693 # Report unbalanced '}'
 1694 /}/ && in_parser_context(NVRAM) {
 1695         error("extra '}'")
 1696 }
 1697 
 1698 # Invalid variable type
 1699 $1 && in_parser_context(SymbolContext) {
 1700         error("unknown type '" $1 "'")
 1701 }
 1702 
 1703 # Generic parse failure
 1704 {
 1705         error("unrecognized statement")
 1706 }
 1707 
 1708 # Create a class instance with the given name
 1709 function class_new(name, superclass, _class) {
 1710         if (_class != null)
 1711                 errorx("class_get() must be called with one or two arguments")
 1712 
 1713         # Look for an existing class instance
 1714         if (name in _g_class_names)
 1715                 errorx("redefining class: " name)
 1716 
 1717         # Create and register the class object
 1718         _class = obj_new(superclass)
 1719         _g_class_names[name] = _class
 1720         _g_obj[_class,OBJ_IS_CLS] = 1
 1721         _g_obj[_class,CLS_NAME] = name
 1722 
 1723         return (_class)
 1724 }
 1725 
 1726 # Return the class instance with the given name
 1727 function class_get(name) {
 1728         if (name in _g_class_names)
 1729                 return (_g_class_names[name])
 1730 
 1731         errorx("no such class " name)
 1732 }
 1733 
 1734 # Return the name of cls
 1735 function class_get_name(cls) {
 1736         if (cls == null) {
 1737                 warnx("class_get_name() called with null class")
 1738                 return "<null>"
 1739         }
 1740 
 1741         if (!obj_is_class(cls))
 1742                 errorx(cls " is not a class object")
 1743 
 1744         return (_g_obj[cls,CLS_NAME])
 1745 }
 1746 
 1747 # Return true if the given property property ID is defined on class
 1748 function class_has_prop_id(class, prop_id, _super) {
 1749         if (_super != null)
 1750                 errorx("class_has_prop_id() must be called with two arguments")
 1751 
 1752         if (class == null)
 1753                 return (0)
 1754 
 1755         if (prop_id == null)
 1756                 return (0)
 1757 
 1758         # Check class<->prop cache
 1759         if ((class, prop_id) in _g_class_prop_cache)
 1760                 return (1)
 1761 
 1762         # Otherwise, slow path
 1763         if (!obj_is_class(class))
 1764                 errorx(class " is not a class object")
 1765 
 1766         if (_super != null)
 1767                 errorx("class_has_prop_id() must be called with two arguments")
 1768 
 1769         for (_super = class; _super != null; _super = obj_get_class(_super)) {
 1770                 if (!((_super,CLS_PROP,prop_id) in _g_obj))
 1771                         continue
 1772 
 1773                 # Found; add to class<->prop cache
 1774                 _g_class_prop_cache[class,prop_id] = 1
 1775                 return (1)
 1776         }
 1777 
 1778         return (0)
 1779 }
 1780 
 1781 # Return true if the given property prop is defined on class
 1782 function class_has_property(class, prop) {
 1783         if (!(PROP_ID in prop))
 1784                 return (0)
 1785 
 1786         return (class_has_prop_id(class, prop[PROP_ID]))
 1787 }
 1788 
 1789 # Define a `prop` on `class` with the given `name` string
 1790 function class_add_prop(class, prop, name, _prop_id) {
 1791         if (_prop_id != null)
 1792                 errorx("class_add_prop() must be called with three arguments")
 1793 
 1794         # Check for duplicate property definition
 1795         if (class_has_property(class, prop))
 1796                 errorx("property " prop[PROP_NAME] " already defined on " \
 1797                     class_get_name(class))
 1798 
 1799         # Init property IDs
 1800         if (_g_prop_ids == null)
 1801                 _g_prop_ids = 1
 1802 
 1803         # Get (or create) new property entry
 1804         if (name in _g_prop_names) {
 1805                 _prop_id = _g_prop_names[name]
 1806         } else {
 1807                 _prop_id = _g_prop_ids++
 1808                 _g_prop_names[name] = _prop_id
 1809                 _g_props[_prop_id] = name
 1810 
 1811                 prop[PROP_NAME] = name
 1812                 prop[PROP_ID]   = _prop_id
 1813         }
 1814 
 1815         # Add to class definition
 1816         _g_obj[class,CLS_PROP,prop[PROP_ID]] = name
 1817         return (name)
 1818 }
 1819 
 1820 # Return the property ID for a given class-defined property
 1821 function class_get_prop_id(class, prop) {
 1822         if (class == null)
 1823                 errorx("class_get_prop_id() on null class")
 1824 
 1825         if (!class_has_property(class, prop)) {
 1826                 errorx("requested undefined property '" prop[PROP_NAME] "on " \
 1827                     class_get_name(class))
 1828         }
 1829 
 1830         return (prop[PROP_ID])
 1831 }
 1832 
 1833 # Return the property ID for a given class-defined property name
 1834 function class_get_named_prop_id(class, name, _prop_id) {
 1835         if (class == null)
 1836                 errorx("class_get_prop_id() on null class")
 1837 
 1838         if (!(name in _g_prop_names))
 1839                 errorx("requested undefined property '" name "'")
 1840 
 1841         _prop_id = _g_prop_names[name]
 1842 
 1843         if (!class_has_prop_id(class, _prop_id)) {
 1844                 errorx("requested undefined property '" _g_props[_prop_id] \
 1845                     "' on " class_get_name(class))
 1846         }
 1847 
 1848         return (_prop_id)
 1849 }
 1850 
 1851 # Create a new instance of the given class
 1852 function obj_new(class, _obj) {
 1853         if (_obj != null)
 1854                 errorx("obj_new() must be called with one argument")
 1855 
 1856         if (_g_obj_ids == null)
 1857                 _g_obj_ids = 1
 1858 
 1859         # Assign ID and set superclass
 1860         _obj = _g_obj_ids++
 1861         _g_obj[_obj,OBJ_SUPER] = class
 1862 
 1863         return (_obj)
 1864 }
 1865 
 1866 # obj_delete() support for Map instances
 1867 function _obj_delete_map(obj, _prefix, _key) {
 1868         obj_assert_class(obj, Map)
 1869         _prefix = "^" obj SUBSEP
 1870         for (_key in _g_maps) {
 1871                 if (!match(_key, _prefix) && _key != obj)
 1872                         continue
 1873                 delete _g_maps[_key]
 1874         }
 1875 }
 1876 
 1877 # obj_delete() support for Array instances
 1878 function _obj_delete_array(obj, _size, _i) {
 1879         obj_assert_class(obj, Array)
 1880         _size = array_size(obj)
 1881 
 1882         for (_i = 0; _i < _size; _i++)
 1883                 delete _g_arrays[obj,OBJ_PROP,_i]
 1884 }
 1885 
 1886 # Destroy all metadata associated with the given object
 1887 function obj_delete(obj, _prop_id, _prop_name, _prefix, _key, _size, _i) {
 1888         if (obj_is_class(obj))
 1889                 errorx("cannot delete class objects")
 1890 
 1891         # Handle classes that use external global array storage
 1892         # for effeciency
 1893         if (obj_is_instanceof(obj, Map)) {
 1894                 _obj_delete_map(obj)
 1895         } else if (obj_is_instanceof(obj, Array)) {
 1896                 _obj_delete_array(obj)
 1897         }
 1898 
 1899         # Delete all object properties
 1900         for (_prop_name in _g_prop_names) {
 1901                 if (!obj_has_prop_id(obj, _prop_id))
 1902                         continue
 1903 
 1904                 _prop_id = _g_prop_names[_prop_name]
 1905                 delete _g_obj[obj,OBJ_PROP,_prop_id]
 1906                 delete _g_obj_nr[obj,OBJ_PROP,_prop_id]
 1907         }
 1908 
 1909         # Delete instance state
 1910         delete _g_obj[obj,OBJ_IS_CLS]
 1911         delete _g_obj[obj,OBJ_SUPER]
 1912 }
 1913 
 1914 # Print an object's unique ID, class, and properties to
 1915 # stdout
 1916 function obj_dump(obj, _pname, _prop_id, _prop_val) {
 1917         print(class_get_name(obj_get_class(obj)) "<" obj ">:")
 1918 
 1919         # Dump all properties
 1920         for (_pname in _g_prop_names) {
 1921                 _prop_id = _g_prop_names[_pname]
 1922 
 1923                 if (!obj_has_prop_id(obj, _prop_id))
 1924                         continue
 1925 
 1926                 _prop_val = prop_get(obj, _prop_id)
 1927                 printf("\t%s: %s\n", _pname, _prop_val)
 1928         }
 1929 }
 1930 
 1931 # Return true if obj is a class object
 1932 function obj_is_class(obj) {
 1933         return (_g_obj[obj,OBJ_IS_CLS] == 1)
 1934 }
 1935 
 1936 # Return the class of obj, if any.
 1937 function obj_get_class(obj) {
 1938         if (obj == null)
 1939                 errorx("obj_get_class() on null object")
 1940         return (_g_obj[obj,OBJ_SUPER])
 1941 }
 1942 
 1943 # Return true if obj is an instance of the given class
 1944 function obj_is_instanceof(obj, class, _super) {
 1945         if (_super != null)
 1946                 errorx("obj_is_instanceof() must be called with two arguments")
 1947 
 1948         if (!obj_is_class(class))
 1949                 errorx(class " is not a class object")
 1950 
 1951         if (obj == null) {
 1952                 errorx("obj_is_instanceof() called with null obj (class " \
 1953                     class_get_name(class) ")")
 1954         }
 1955 
 1956         for (_super = obj_get_class(obj); _super != null;
 1957              _super = obj_get_class(_super))
 1958         {
 1959                 if (_super == class)
 1960                         return (1)
 1961         }
 1962 
 1963         return (0)
 1964 }
 1965 
 1966 # Default object shallow equality implementation. Returns true if the two
 1967 # objects share a common superclass and have identity equality across all defined
 1968 # properties.
 1969 function obj_trivially_equal(lhs, rhs, _class, _pname, _prop_id) {
 1970         # Simple case
 1971         if (lhs == rhs)
 1972                 return (1)
 1973 
 1974         # Must share a common superclass
 1975         _class = obj_get_class(lhs)
 1976         if (_class != obj_get_class(rhs))
 1977                 return (0)
 1978 
 1979         # Compare all properties
 1980         _prop_count = 0
 1981         for (_pname in _g_prop_names) {
 1982                 _prop_id = _g_prop_names[_pname]
 1983 
 1984                 if (!class_has_prop_id(_class, _prop_id))
 1985                         continue
 1986 
 1987                 if (prop_get(lhs, _prop_id) != prop_get(rhs, _prop_id))
 1988                         return (0)
 1989         }
 1990 
 1991         # All properties are trivially equal
 1992         return (1)
 1993 }
 1994 
 1995 
 1996 # Return a debug string representation of an object's unique ID, class, and
 1997 # properties
 1998 function obj_to_string(obj, _pname, _prop_id, _prop_val, _prop_count, _result) {
 1999         _result = class_get_name(obj_get_class(obj)) "<" obj ">: { "
 2000 
 2001         # Fetch all properties
 2002         _prop_count = 0
 2003         for (_pname in _g_prop_names) {
 2004                 _prop_id = _g_prop_names[_pname]
 2005 
 2006                 if (!obj_has_prop_id(obj, _prop_id))
 2007                         continue
 2008 
 2009                 if (_prop_count >= 0)
 2010                         _result = _result ", "
 2011 
 2012                 _result = _result sprintf("\t%s: %s\n", _pname, _prop_val)
 2013                 _prop_count++
 2014         }
 2015 
 2016         return (_result " }")
 2017 }
 2018 
 2019 # Assert that obj is an instance of the given class
 2020 function obj_assert_class(obj, class) {
 2021         if (!obj_is_instanceof(obj, class)) {
 2022                 errorx(class_get_name(obj_get_class(obj)) "<" obj "> is not " \
 2023                     "an instance of " class_get_name(class))
 2024         }
 2025 }
 2026 
 2027 # Return true if the given property prop is defined by the object's superclass
 2028 function obj_has_property(obj, prop, _class) {
 2029         if (obj == null)
 2030                 errorx("obj_has_property() on null object")
 2031 
 2032         _class = obj_get_class(obj)
 2033         return (class_has_property(_class, prop))
 2034 }
 2035 
 2036 # Return true if the given property ID is defined by the object's superclass
 2037 function obj_has_prop_id(obj, prop_id, _class) {
 2038         if (obj == null)
 2039                 errorx("obj_has_prop_id() on null object")
 2040 
 2041         _class = obj_get_class(obj)
 2042         return (class_has_prop_id(_class, prop_id))
 2043 }
 2044 
 2045 # Return the line (NR) at which a given property ID was set on the object
 2046 # Will throw an error if the property has not been set on obj
 2047 function obj_get_prop_id_nr(obj, prop_id) {
 2048         if (obj == null)
 2049                 errorx("obj_get_prop_id_nr() on null object")
 2050 
 2051         if (!obj_has_prop_id(obj, prop_id)) {
 2052                 errorx("requested undefined property '" _g_props[prop_id] \
 2053                     "' (" prop_id ") on " obj_to_string(obj))
 2054         }
 2055 
 2056         # Fetch NR
 2057         if ((obj,OBJ_PROP,prop_id) in _g_obj_nr)
 2058                 return (_g_obj_nr[obj,OBJ_PROP,prop_id])
 2059 
 2060         errorx("property '" _g_props[prop_id] "' (" prop_id ") not " \
 2061             "previously set on " obj_to_string(obj))
 2062 }
 2063 
 2064 # Return the line (NR) at which a given property was set on the object
 2065 # Will throw an error if the property has not been set on obj
 2066 function obj_get_prop_nr(obj, prop) {
 2067         return (obj_get_prop_id_nr(obj, prop[PROP_ID]))
 2068 }
 2069 
 2070 # Return an abstract property ID for a given property
 2071 function obj_get_prop_id(obj, prop) {
 2072         if (obj == null)
 2073                 errorx("obj_get_prop_id() on null object")
 2074 
 2075         return (class_get_prop_id(obj_get_class(obj), prop))
 2076 }
 2077 
 2078 
 2079 # Return the property ID for a given property name
 2080 function obj_get_named_prop_id(obj, name) {
 2081         if (obj == null)
 2082                 errorx("obj_get_named_prop_id() on null object")
 2083 
 2084         return (class_get_named_prop_id(obj_get_class(obj), name))
 2085 }
 2086 
 2087 # Set a property on obj
 2088 function set(obj, prop, value, _class) {
 2089         return (prop_set(obj, prop[PROP_ID], value))
 2090 }
 2091 
 2092 # Get a property value defined on obj
 2093 function get(obj, prop, _class) {
 2094         return (prop_get(obj, prop[PROP_ID]))
 2095 }
 2096 
 2097 # Set a property on obj, using a property ID returned by obj_get_prop_id() or
 2098 # class_get_prop_id()
 2099 function prop_set(obj, prop_id, value, _class) {
 2100         if (obj == null) {
 2101                 errorx("setting property '" _g_props[prop_id] \
 2102                     "' on null object")
 2103         }
 2104 
 2105         _class = obj_get_class(obj)
 2106         if (_class == null)
 2107                 errorx(obj " has no superclass")
 2108 
 2109         if (!class_has_prop_id(_class, prop_id)) {
 2110                 errorx("requested undefined property '" _g_props[prop_id] \
 2111                     "' (" prop_id ") on " class_get_name(_class))
 2112         }
 2113 
 2114         # Track the line on which the property was set
 2115         _g_obj_nr[obj,OBJ_PROP,prop_id] = NR
 2116         _g_obj[obj,OBJ_PROP,prop_id] = value
 2117 }
 2118 
 2119 # Convert a property ID to a property path.
 2120 function prop_id_to_path(prop_id) {
 2121         if (!(prop_id in _g_props))
 2122                 errorx("'" prop_id "' is not a property ID")
 2123 
 2124         # Convert to path string representation
 2125         return (""prop_id)
 2126 }
 2127 
 2128 # Convert a property to a property path.
 2129 function prop_to_path(prop) {
 2130         if (!(PROP_ID in prop))
 2131                 errorx("prop_to_path() called with non-property head")
 2132 
 2133         return (prop_id_to_path(prop[PROP_ID]))
 2134 }
 2135 
 2136 # Create a property path from head and tail properties
 2137 # Additional properties may be appended via prop_path_append() or
 2138 # prop_path_append_id()
 2139 function prop_path_create(head, tail) {
 2140         if (!(PROP_ID in head))
 2141                 errorx("prop_path() called with non-property head")
 2142 
 2143         if (!(PROP_ID in tail))
 2144                 errorx("prop_path() called with non-property tail")
 2145 
 2146         return (head[PROP_ID] SUBSEP tail[PROP_ID])
 2147 }
 2148 
 2149 # Append a property to the given property path
 2150 function prop_path_append(path, tail) {
 2151         if (!(PROP_ID in tail))
 2152                 errorx("prop_path_append() called with non-property tail")
 2153 
 2154         return (prop_path_append_id(path, tail[PROP_ID]))
 2155 }
 2156 
 2157 # Append a property ID to the given property path
 2158 function prop_path_append_id(path, tail_id) {
 2159         if (!(tail_id in _g_props))
 2160                 errorx("'" tail_id "' is not a property ID")
 2161 
 2162         return (path SUBSEP tail_id)
 2163 }
 2164 
 2165 # Fetch a value from obj using a property path previously returned by
 2166 # prop_path_create(), prop_to_path(), etc.
 2167 function prop_get_path(obj, prop_path, _class, _prop_ids, _nprop_ids, _next,
 2168     _prop_head, _prop_len, _prop_tail)
 2169 {
 2170         if (obj == null) {
 2171                 errorx("requested property path '" \
 2172                     gsub(SUBSEP, ".", prop_path)  "' on null object")
 2173         }
 2174 
 2175         # Try the cache first
 2176         _class = obj_get_class(obj)
 2177         if ((_class,prop_path,PPATH_HEAD) in _g_ppath_cache) {
 2178                 _prop_head = _g_ppath_cache[_class,prop_path,PPATH_HEAD]
 2179                 _next = prop_get(obj, _prop_head)
 2180 
 2181                 if ((_class,prop_path,PPATH_TAIL) in _g_ppath_cache) {
 2182                         _prop_tail = _g_ppath_cache[_class,prop_path,PPATH_TAIL]
 2183                         return (prop_get_path(_next, _prop_tail))
 2184                 }
 2185 
 2186                 return (_next)
 2187         }
 2188 
 2189         # Parse the head/tail of the property path and add to cache
 2190         _nprop_ids = split(prop_path, _prop_ids, SUBSEP)
 2191         if (_nprop_ids == 0)
 2192                 errorx("empty property path")
 2193         _prop_head = _prop_ids[1]
 2194         _g_ppath_cache[_class,prop_path,PPATH_HEAD] = _prop_head
 2195 
 2196         if (_nprop_ids > 1) {
 2197                 _prop_len = length(_prop_head)
 2198                 _prop_tail = substr(prop_path, _prop_len+2)
 2199 
 2200                 # Add to cache
 2201                 _g_ppath_cache[_class,prop_path,PPATH_TAIL] = _prop_tail
 2202         }
 2203 
 2204         # Recursively call out implementation, this time fetching from
 2205         # cache
 2206         return (prop_get_path(obj, prop_path))
 2207 }
 2208 
 2209 # Fetch a value property value from obj, using a property ID returned by
 2210 # obj_get_prop_id() or class_get_prop_id()
 2211 function prop_get(obj, prop_id, _class) {
 2212         if (obj == null) {
 2213                 errorx("requested property '" _g_props[prop_id] \
 2214                     "' on null object")
 2215         }
 2216 
 2217         _class = obj_get_class(obj)
 2218         if (_class == null)
 2219                 errorx(obj " has no superclass")
 2220 
 2221         if (!class_has_prop_id(_class, prop_id)) {
 2222                 errorx("requested undefined property '" _g_props[prop_id] \
 2223                     "' (" prop_id ") on " class_get_name(_class))
 2224         }
 2225 
 2226         return (_g_obj[obj,OBJ_PROP,prop_id])
 2227 }
 2228 
 2229 # Create a new MacroType instance
 2230 function macro_type_new(name, const_suffix, _obj) {
 2231         _obj = obj_new(MacroType)
 2232 
 2233         set(_obj, p_name, name)
 2234         set(_obj, p_const_suffix, const_suffix)
 2235 
 2236         return (_obj)
 2237 }
 2238 
 2239 # Create a new MacroDefine instance
 2240 function macro_new(name, value, _obj) {
 2241         _obj = obj_new(MacroDefine)
 2242         set(_obj, p_name, name)
 2243         set(_obj, p_value, value)
 2244 
 2245         return (_obj)
 2246 }
 2247 
 2248 # Create an empty array; this uses _g_arrays to store integer
 2249 # keys/values under the object's property prefix.
 2250 function array_new(_obj) {
 2251         _obj = obj_new(Array)
 2252         set(_obj, p_count, 0)
 2253 
 2254         return (_obj)
 2255 }
 2256 
 2257 # Return the number of elements in the array
 2258 function array_size(array) {
 2259         obj_assert_class(array, Array)
 2260         return (get(array, p_count))
 2261 }
 2262 
 2263 # Return true if the array is empty
 2264 function array_empty(array) {
 2265         return (array_size(array) == 0)
 2266 }
 2267 
 2268 # Append a value to the array
 2269 function array_append(array, value, _i) {
 2270         obj_assert_class(array, Array)
 2271 
 2272         _i = get(array, p_count)
 2273         _g_arrays[array,OBJ_PROP,_i] = value
 2274         set(array, p_count, _i+1)
 2275 }
 2276 
 2277 # Set an array value
 2278 # An error will be thrown if the idx is outside array bounds
 2279 function array_set(array, idx, value) {
 2280         obj_assert_class(array, Array)
 2281 
 2282         if (!((array,OBJ_PROP,idx) in _g_arrays))
 2283                 errorx(idx " out of range of array " obj_to_string(array))
 2284 
 2285         _g_arrays[array,OBJ_PROP,idx] = value
 2286 }
 2287 
 2288 # Return value at the given index from the array
 2289 # An error will be thrown if 'idx' is outside the array bounds
 2290 function array_get(array, idx) {
 2291         obj_assert_class(array, Array)
 2292 
 2293         if (!((array,OBJ_PROP,idx) in _g_arrays))
 2294                 errorx(idx " out of range of array " obj_to_string(array))
 2295 
 2296         return (_g_arrays[array,OBJ_PROP,idx])
 2297 }
 2298 
 2299 
 2300 #
 2301 # Sort an array, using standard awk comparison operators over its values.
 2302 #
 2303 # If `prop_path*` is non-NULL, the corresponding property path (or property ID)
 2304 # will be fetched from each array element and used as the sorting value.
 2305 #
 2306 # If multiple property paths are specified, the array is first sorted by
 2307 # the first path, and then any equal values are sorted by the second path,
 2308 # and so on.
 2309 #
 2310 function array_sort(array, prop_path0, prop_path1, prop_path2, _size) {
 2311         obj_assert_class(array, Array)
 2312 
 2313         if (_size != null)
 2314                 errorx("no more than three property paths may be specified")
 2315 
 2316         _size = array_size(array)
 2317         if (_size <= 1)
 2318                 return
 2319 
 2320         _qsort(array, prop_path0, prop_path1, prop_path2, 0, _size-1)
 2321 }
 2322 
 2323 function _qsort_get_key(array, idx, prop_path, _v) {
 2324         _v = array_get(array, idx)
 2325         
 2326         if (prop_path == null)
 2327                 return (_v)
 2328 
 2329         return (prop_get_path(_v, prop_path))
 2330 }
 2331 
 2332 function _qsort_compare(array, lhs_idx, rhs_val, ppath0, ppath1, ppath2,
 2333     _lhs_val, _rhs_prop_val)
 2334 {
 2335         _lhs_val = _qsort_get_key(array, lhs_idx, ppath0)
 2336         if (ppath0 == null)
 2337                 _rhs_prop_val = rhs_val
 2338         else
 2339                 _rhs_prop_val = prop_get_path(rhs_val, ppath0)
 2340 
 2341         if (_lhs_val == _rhs_prop_val && ppath1 != null) {
 2342                 _lhs_val = _qsort_get_key(array, lhs_idx, ppath1)
 2343                 _rhs_prop_val = prop_get_path(rhs_val, ppath1)
 2344 
 2345                 if (_lhs_val == _rhs_prop_val && ppath2 != null) {
 2346                         _lhs_val = _qsort_get_key(array, lhs_idx, ppath2)
 2347                         _rhs_prop_val = prop_get_path(rhs_val, ppath2)
 2348                 }
 2349         }
 2350 
 2351         if (_lhs_val < _rhs_prop_val)
 2352                 return (-1)
 2353         else if (_lhs_val > _rhs_prop_val)
 2354                 return (1)
 2355         else
 2356                 return (0)
 2357 }
 2358 
 2359 function _qsort(array, ppath0, ppath1, ppath2, first, last, _qpivot,
 2360     _qleft, _qleft_val, _qright, _qright_val)
 2361 {
 2362         if (first >= last)
 2363                 return
 2364 
 2365         # select pivot element
 2366         _qpivot = int(first + int((last-first+1) * rand()))
 2367         _qleft = first
 2368         _qright = last
 2369 
 2370         _qpivot_val = array_get(array, _qpivot)
 2371 
 2372         # partition
 2373         while (_qleft <= _qright) {
 2374                 while (_qsort_compare(array, _qleft, _qpivot_val, ppath0, ppath1,
 2375                     ppath2) < 0)
 2376                 {
 2377                         _qleft++
 2378                 }
 2379 
 2380                 while (_qsort_compare(array, _qright, _qpivot_val, ppath0, ppath1,
 2381                     ppath2) > 0)
 2382                 {
 2383                         _qright--
 2384                 }
 2385 
 2386                 # swap
 2387                 if (_qleft <= _qright) {
 2388                         _qleft_val = array_get(array, _qleft)
 2389                         _qright_val = array_get(array, _qright)
 2390                         
 2391                         array_set(array, _qleft, _qright_val)
 2392                         array_set(array, _qright, _qleft_val)
 2393 
 2394                         _qleft++
 2395                         _qright--
 2396                 }
 2397         }
 2398 
 2399         # sort the partitions
 2400         _qsort(array, ppath0, ppath1, ppath2, first, _qright)
 2401         _qsort(array, ppath0, ppath1, ppath2, _qleft, last)
 2402 }
 2403 
 2404 
 2405 #
 2406 # Join all array values with the given separator
 2407 #
 2408 # If `prop_path` is non-NULL, the corresponding property path (or property ID)
 2409 # will be fetched from each array value and included in the result, rather than
 2410 # immediate array value
 2411 #
 2412 function array_join(array, sep, prop_path, _i, _size, _value, _result) {
 2413         obj_assert_class(array, Array)
 2414 
 2415         _result = ""
 2416         _size = array_size(array)
 2417         for (_i = 0; _i < _size; _i++) {
 2418                 # Fetch the value (and optionally, a target property)
 2419                 _value = array_get(array, _i)
 2420                 if (prop_path != null)
 2421                         _value = prop_get_path(_value, prop_path)
 2422 
 2423                 if (_i+1 < _size)
 2424                         _result = _result _value sep
 2425                 else
 2426                         _result = _result _value
 2427         }
 2428 
 2429         return (_result)
 2430 }
 2431 
 2432 # Return the first value in the array, or null if empty
 2433 function array_first(array) {
 2434         obj_assert_class(array, Array)
 2435 
 2436         if (array_size(array) == 0)
 2437                 return (null)
 2438         else 
 2439                 return (array_get(array, 0))
 2440 }
 2441 
 2442 # Return the last value in the array, or null if empty
 2443 function array_tail(list, _size) {
 2444         obj_assert_class(array, Array)
 2445 
 2446         _size = array_size(array)
 2447         if (_size == 0)
 2448                 return (null)
 2449         else 
 2450                 return (array_get(array, _size-1))
 2451 }
 2452 
 2453 # Create an empty hash table; this uses the _g_maps array to store arbitrary
 2454 # keys/values under the object's property prefix.
 2455 function map_new(_obj) {
 2456         _obj = obj_new(Map)
 2457         return (_obj)
 2458 }
 2459 
 2460 # Add `key` with `value` to `map`
 2461 function map_set(map, key, value) {
 2462         obj_assert_class(map, Map)
 2463         _g_maps[map,OBJ_PROP,key] = value
 2464 }
 2465 
 2466 # Remove `key` from the map
 2467 function map_remove(map, key) {
 2468         obj_assert_class(map, Map)
 2469         delete _g_maps[map,OBJ_PROP,key]
 2470 }
 2471 
 2472 # Return true if `key` is found in `map`, false otherwise
 2473 function map_contains(map, key) {
 2474         obj_assert_class(map, Map)
 2475         return ((map,OBJ_PROP,key) in _g_maps)
 2476 }
 2477 
 2478 # Fetch the value of `key` from the map. Will throw an error if the
 2479 # key does not exist
 2480 function map_get(map, key) {
 2481         obj_assert_class(map, Map)
 2482         return _g_maps[map,OBJ_PROP,key]
 2483 }
 2484 
 2485 # Create and return a new list containing all defined values in `map`
 2486 function map_to_array(map, _key, _prefix, _values) {
 2487         obj_assert_class(map, Map)
 2488 
 2489         _values = array_new()
 2490         _prefix = "^" map SUBSEP OBJ_PROP SUBSEP
 2491         for (_key in _g_maps) {
 2492                 if (!match(_key, _prefix))
 2493                         continue
 2494 
 2495                 array_append(_values, _g_maps[_key])
 2496         }
 2497 
 2498         return (_values)
 2499 }
 2500 
 2501 # Create a new Type instance
 2502 function type_new(name, width, signed, constant, array_constant, fmt, mask,
 2503     constant_value, array_constant_value, _obj)
 2504 {
 2505         obj_assert_class(fmt, Fmt)
 2506 
 2507         _obj = obj_new(Type)
 2508         set(_obj, p_name, name)
 2509         set(_obj, p_width, width)
 2510         set(_obj, p_signed, signed)
 2511         set(_obj, p_const, constant)
 2512         set(_obj, p_const_val, constant_value)
 2513         set(_obj, p_array_const, array_constant)
 2514         set(_obj, p_array_const_val, array_constant_value)
 2515         set(_obj, p_default_fmt, fmt)
 2516         set(_obj, p_mask, mask)
 2517 
 2518         return (_obj)
 2519 }
 2520 
 2521 # Return true if two types are equal
 2522 function type_equal(lhs, rhs) {
 2523         # Simple case
 2524         if (lhs == rhs)
 2525                 return (1)
 2526 
 2527         # Must share a common class
 2528         if (obj_get_class(lhs) != obj_get_class(rhs))
 2529                 return (0)
 2530 
 2531         # Handle ArrayType equality
 2532         if (obj_is_instanceof(lhs, ArrayType)) {
 2533                 # Size must be equal
 2534                 if (get(lhs, p_count) != get(rhs, p_count))
 2535                         return (0)
 2536 
 2537                 # The base types must be equal
 2538                 return (type_equal(type_get_base(lhs), type_get_base(rhs)))
 2539         }
 2540 
 2541         # Handle Type equality -- we just check for trivial identity
 2542         # equality of all members
 2543         obj_assert_class(lhs, Type)
 2544         return (obj_trivially_equal(lhs, rhs))
 2545 }
 2546 
 2547 # Return the type's default value mask. If the type is an array type,
 2548 # the default mask of the base type will be returned.
 2549 function type_get_default_mask(type) {
 2550         if (obj_is_instanceof(type, ArrayType))
 2551                 return (type_get_default_mask(type_get_base(type)))
 2552 
 2553         obj_assert_class(type, Type)
 2554         return (get(type, p_mask))
 2555 }
 2556 
 2557 # Return the type's C constant representation
 2558 function type_get_const(type) {
 2559         if (obj_is_instanceof(type, ArrayType))
 2560                 return (get(type_get_base(type), p_array_const))
 2561 
 2562         obj_assert_class(type, Type)
 2563         return (get(type, p_const))
 2564 }
 2565 
 2566 # Return the type's C constant integer value
 2567 function type_get_const_val(type) {
 2568         if (obj_is_instanceof(type, ArrayType))
 2569                 return (get(type_get_base(type), p_array_const_val))
 2570 
 2571         obj_assert_class(type, Type)
 2572         return (get(type, p_const_val))
 2573 }
 2574 
 2575 # Return an array type's element count, or 1 if the type is not
 2576 # an array type
 2577 function type_get_nelem(type) {
 2578         if (obj_is_instanceof(type, ArrayType))
 2579                 return (get(type, p_count))
 2580 
 2581         obj_assert_class(type, Type)
 2582         return (1)
 2583 }
 2584 
 2585 # Return the base type for a given type instance.
 2586 function type_get_base(type) {
 2587         if (obj_is_instanceof(type, ArrayType))
 2588                 return (type_get_base(get(type, p_type)))
 2589 
 2590         obj_assert_class(type, Type)
 2591         return (type)
 2592 }
 2593 
 2594 # Return the default fmt for a given type instance
 2595 function type_get_default_fmt(type, _base, _fmt, _array_fmt) {
 2596         _base = type_get_base(type)
 2597         _fmt = get(_base, p_default_fmt)
 2598 
 2599         if (obj_is_instanceof(type, ArrayType)) {
 2600                 _array_fmt = get(_fmt, p_array_fmt)
 2601                 if (_array_fmt != null)
 2602                         _fmt = _array_fmt
 2603         }
 2604 
 2605         return (_fmt)
 2606 }
 2607 
 2608 # Return a string representation of the given type
 2609 function type_to_string(type, _base_type) {
 2610         if (obj_is_instanceof(type, ArrayType)) {
 2611                 _base_type = type_get_base(type)
 2612                 return (type_to_string(_base_type) "[" get(type, p_count) "]")
 2613         }
 2614         return get(type, p_name)
 2615 }
 2616 
 2617 # Return true if type `rhs` is can be coerced to type `lhs` without data
 2618 # loss
 2619 function type_can_represent(lhs, rhs) {
 2620         # Must be of the same class (Type or ArrayType)
 2621         if (obj_get_class(lhs) != obj_get_class(rhs))
 2622                 return (0)
 2623 
 2624         if (obj_is_instanceof(lhs, ArrayType)) {
 2625                 # The base types must have a representable relationship
 2626                 if (!type_can_represent(type_get_base(lhs), type_get_base(rhs)))
 2627                         return (0)
 2628 
 2629                 # The lhs type must be able to represent -at least- as
 2630                 # many elements as the RHS type
 2631                 if (get(lhs, p_count) < get(rhs, p_count))
 2632                         return (0)
 2633 
 2634                 return (1)
 2635         }
 2636 
 2637         # A signed type could represent the full range of a smaller unsigned
 2638         # type, but we don't bother; the two should agree when used in a SROM
 2639         # layout. Instead simply assert that both are signed or unsigned.
 2640         if (get(lhs, p_signed) != get(rhs, p_signed))
 2641                 return (0)
 2642 
 2643         # The `rhs` type must be equal or smaller in width to the `lhs` type
 2644         if (get(lhs, p_width) < get(rhs, p_width))
 2645                 return (0)
 2646 
 2647         return (1)
 2648 }
 2649 
 2650 # Create a new ArrayType instance
 2651 function array_type_new(type, count, _obj) {
 2652         _obj = obj_new(ArrayType)
 2653         set(_obj, p_type, type)
 2654         set(_obj, p_count, count)
 2655 
 2656         return (_obj)
 2657 }
 2658 
 2659 #
 2660 # Parse a type string to either the Type, ArrayType, or null if
 2661 # the type is not recognized.
 2662 #
 2663 function parse_type_string(str, _base, _count) {
 2664         if (match(str, ARRAY_REGEX"$") > 0) {
 2665                 # Extract count and base type
 2666                 _count = substr(str, RSTART+1, RLENGTH-2)
 2667                 sub(ARRAY_REGEX"$", "", str)
 2668 
 2669                 # Look for base type
 2670                 if ((_base = type_named(str)) == null)
 2671                         return (null)
 2672 
 2673                 return (array_type_new(_base, int(_count)))
 2674         } else {
 2675                 return (type_named(str))
 2676         }
 2677 }
 2678 
 2679 #
 2680 # Parse a variable name in the form of 'name' or 'name[len]', returning
 2681 # either the provided base_type if no array specifiers are found, or
 2682 # the fully parsed ArrayType.
 2683 #
 2684 function parse_array_type_specifier(str, base_type, _count) {
 2685         if (match(str, ARRAY_REGEX"$") > 0) {
 2686                 # Extract count
 2687                 _count = substr(str, RSTART+1, RLENGTH-2)
 2688                 return (array_type_new(base_type, int(_count)))
 2689         } else {
 2690                 return (base_type)
 2691         }
 2692 }
 2693 
 2694 # Return the type constant for `name`, if any
 2695 function type_named(name, _n, _type) {
 2696         if (name == null)
 2697                 errorx("called type_named() with null name")
 2698 
 2699         if (map_contains(BaseTypes, name))
 2700                 return (map_get(BaseTypes, name))
 2701 
 2702         return (null)   
 2703 }
 2704 
 2705 # Create a new Fmt instance
 2706 function fmt_new(name, symbol, array_fmt, _obj) {
 2707         _obj = obj_new(Fmt)
 2708         set(_obj, p_name, name)
 2709         set(_obj, p_symbol, symbol)
 2710 
 2711         if (array_fmt != null)
 2712                 set(_obj, p_array_fmt, array_fmt)
 2713 
 2714         return (_obj)
 2715 }
 2716 
 2717 
 2718 # Return the Fmt constant for `name`, if any
 2719 function fmt_named(name, _n, _fmt) {
 2720         if (map_contains(ValueFormats, name))
 2721                 return (map_get(ValueFormats, name))
 2722 
 2723         return (null)
 2724 }
 2725 
 2726 # Create a new VFlag instance
 2727 function vflag_new(name, constant, _obj) {
 2728         _obj = obj_new(VFlag)
 2729         set(_obj, p_name, name)
 2730         set(_obj, p_const, constant)
 2731 
 2732         return (_obj)
 2733 }
 2734 
 2735 # Create a new StringConstant AST node
 2736 function stringconstant_new(value, continued, _obj) {
 2737         _obj = obj_new(StringConstant)
 2738         set(_obj, p_value, value)
 2739         set(_obj, p_continued, continued)
 2740         set(_obj, p_line, NR)
 2741 
 2742         return (_obj)
 2743 }
 2744 
 2745 # Create an empty StringConstant AST node to which additional lines
 2746 # may be appended
 2747 function stringconstant_empty(_obj) {
 2748         return (stringconstant_new("", 1))
 2749 }
 2750 
 2751 # Parse an input string and return a new string constant
 2752 # instance
 2753 function stringconstant_parse_line(line, _obj) {
 2754         _obj = stringconstant_empty()
 2755         stringconstant_append_line(_obj, line)
 2756         return (_obj)
 2757 }
 2758 
 2759 # Parse and apend an additional line to this string constant
 2760 function stringconstant_append_line(str, line, _cont, _strbuf, _regex, _eol) {
 2761         obj_assert_class(str, StringConstant)
 2762 
 2763         # Must be marked for continuation
 2764         if (!get(str, p_continued)) {
 2765                 errorx("can't append to non-continuation string '" \
 2766                     get(str, p_value) "'")
 2767         }
 2768 
 2769         _strbuf = get(str, p_value)
 2770 
 2771         # If start of string, look for (and remove) initial double quote
 2772         if (_strbuf == null) {
 2773                 _regex = "^[ \t]*\""
 2774                 if (!sub(_regex, "", line)) {
 2775                         error("expected quoted string")
 2776                 }
 2777         }
 2778 
 2779         # Look for a terminating double quote
 2780         _regex = "([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\""
 2781 
 2782         _eol = match(line, _regex)
 2783         if (_eol > 0) {
 2784                 # Drop everything following the terminating quote
 2785                 line = substr(line, 1, RLENGTH-1)
 2786                 _cont = 0
 2787         } else {
 2788                 # No terminating quote found, continues on next line
 2789                 _cont = 1
 2790         }
 2791 
 2792         # Trim leading and trailing whitespace
 2793         sub(/(^[ \t]+|[ \t]+$)/, "", line)
 2794 
 2795         # Append to existing buffer
 2796         if ((_strbuf = get(str, p_value)) == NULL)
 2797                 set(str, p_value, line)
 2798         else
 2799                 set(str, p_value, _strbuf " " line)
 2800 
 2801         # Update line continuation setting
 2802         set(str, p_continued, _cont)
 2803 }
 2804 
 2805 # Create a new RevRange instance
 2806 function revrange_new(start, end, _obj) {
 2807         _obj = obj_new(RevRange)
 2808         set(_obj, p_start, start)
 2809         set(_obj, p_end, end)
 2810         set(_obj, p_line, NR)
 2811 
 2812         return (_obj)
 2813 }
 2814 
 2815 # Return true if the two revision ranges are equal
 2816 function revrange_equal(lhs, rhs) {
 2817         if (get(lhs, p_start) != get(rhs, p_start))
 2818                 return (0)
 2819 
 2820         if (get(lhs, p_end) != get(rhs, p_end))
 2821                 return (0)
 2822 
 2823         return (1)
 2824 }
 2825 
 2826 # Return true if the requested rev is covered by revrange, false otherwise
 2827 function revrange_contains(range, rev) {
 2828         obj_assert_class(range, RevRange)
 2829 
 2830         if (rev < get(range, p_start))
 2831                 return (0)
 2832         else if (rev > get(range, p_end)) {
 2833                 return (0)
 2834         } else {
 2835                 return (1)
 2836         }
 2837 }
 2838 
 2839 #
 2840 # Return a string representation of the given revision range
 2841 #
 2842 function revrange_to_string(revs, _start, _end) {
 2843         obj_assert_class(revs, RevRange)
 2844 
 2845         _start = get(revs, p_start)
 2846         _end = get(revs, p_end)
 2847 
 2848         if (_start == 0)
 2849                 return ("<= " _end)
 2850         else if (_end == REV_MAX)
 2851                 return (">= " _start)
 2852         else
 2853                 return (_start "-" _end)
 2854 }
 2855 
 2856 # Create a new VarGroup instance
 2857 function var_group_new(name, _obj) {
 2858         _obj = obj_new(VarGroup)
 2859         set(_obj, p_name, name)
 2860         set(_obj, p_vars, array_new())
 2861         set(_obj, p_line, NR)
 2862 
 2863         return (_obj)
 2864 }
 2865 
 2866 # Create a new NVRAM instance
 2867 function nvram_new(_obj, _vars, _v) {
 2868         _obj = obj_new(NVRAM)
 2869         _vars = array_new()
 2870         set(_obj, p_vars, _vars)
 2871         set(_obj, p_var_groups, array_new())
 2872         set(_obj, p_srom_layouts, array_new())
 2873         set(_obj, p_srom_table, map_new())
 2874 
 2875         #
 2876         # Register our implicit variable definitions
 2877         #
 2878 
 2879         # SROM signature offset
 2880         _v = var_new(VAccessInternal, "<sromsig>", UInt16)
 2881         array_append(_vars, _v)
 2882         _g_var_names[get(_v, p_name)] = _v
 2883 
 2884         # SROM CRC8 offset
 2885         _v = var_new(VAccessInternal, "<sromcrc>", UInt8)
 2886         array_append(_vars, _v)
 2887         _g_var_names[get(_v, p_name)] = _v
 2888 
 2889         return (_obj)
 2890 }
 2891 
 2892 # Register a new SROM layout instance
 2893 # An error will be thrown if the layout overlaps any revisions covered
 2894 # by an existing instance.
 2895 function nvram_add_srom_layout(nvram, layout, _table, _revs, _start, _end, _i) {
 2896         obj_assert_class(nvram, NVRAM)
 2897         obj_assert_class(layout, SromLayout)
 2898 
 2899         # revision:layout hash table
 2900         _table = get(nvram, p_srom_table)
 2901 
 2902         # register the layout's revisions
 2903         _revs = get(layout, p_revisions)
 2904         _start = get(_revs, p_start)
 2905         _end = get(_revs, p_end)
 2906 
 2907         for (_i = _start; _i <= _end; _i++) {
 2908                 if (map_contains(_table, _i)) {
 2909                         error("SROM layout redeclares layout for revision '" \
 2910                             _i "' (originally declared on line " \
 2911                             get(map_get(_table, _i), p_line) ")")
 2912                 }
 2913 
 2914                 map_set(_table, _i, layout)
 2915         }
 2916 
 2917         # append to srom_layouts
 2918         array_append(get(nvram, p_srom_layouts), layout)
 2919 }
 2920 
 2921 # Return the first SROM layout registered for a given SROM revision,
 2922 # or null if no matching layout is found
 2923 function nvram_get_srom_layout(nvram, revision, _layouts, _nlayouts, _layout,
 2924     _i)
 2925 {
 2926         obj_assert_class(nvram, NVRAM)
 2927 
 2928         _layouts = get(nvram, p_srom_layouts)
 2929         _nlayouts = array_size(_layouts)
 2930         for (_i = 0; _i < _nlayouts; _i++) {
 2931                 _layout = array_get(_layouts, _i)
 2932 
 2933                 if (srom_layout_has_rev(_layout, revision))
 2934                         return (_layout)
 2935         }
 2936 
 2937         # Not found
 2938         return (null)
 2939 }
 2940 
 2941 # Create a new Var instance
 2942 function var_new(access, name, type, _obj) {
 2943         obj_assert_class(access, VAccess)
 2944 
 2945         # Validate the variable identifier
 2946         #
 2947         # The access modifier dictates the permitted identifier format.
 2948         #   VAccessInternal:            <ident>
 2949         #   VAccess(Public|Private):    ident
 2950         if (access != VAccessInternal && name ~ SVAR_IDENT_REGEX) {
 2951                 error("invalid identifier '"name"'; did you mean to " \
 2952                     "mark this variable as internal?")
 2953         } else if (access == VAccessInternal) {
 2954                 if (name !~ SVAR_IDENT_REGEX)
 2955                         error("invalid identifier '"name"' for internal " \
 2956                         "variable; did you mean '<" name ">'?")
 2957         } else if (name !~ VAR_IDENT_REGEX) {
 2958                 error("invalid identifier '"name"'")
 2959         }
 2960 
 2961         _obj = obj_new(Var)
 2962         set(_obj, p_access, access)
 2963         set(_obj, p_name, name)
 2964         set(_obj, p_type, type)
 2965         set(_obj, p_line, NR)
 2966 
 2967         return (_obj)
 2968 }
 2969 
 2970 # Return true if var is internal-only, and should not be included
 2971 # in any output (e.g. has an access specifier of VAccessInternal).
 2972 function var_is_internal(var) {
 2973         return (get(var, p_access) == VAccessInternal)
 2974 }
 2975 
 2976 # Return true if `var` has an array type
 2977 function var_has_array_type(var, _vtype) {
 2978         obj_assert_class(var, Var)
 2979         _vtype = get(var, p_type)
 2980         return (obj_is_instanceof(_vtype, ArrayType))
 2981 }
 2982 
 2983 # Return the number of array elements defined by this variable's type,
 2984 # or 1 if the variable does not have an array type.
 2985 function var_get_array_len(var) {
 2986         obj_assert_class(var, Var)
 2987         return (type_get_nelem(get(var, p_type)))
 2988 }
 2989 
 2990 # Return the fmt for var. If not explicitly set on var, will return then
 2991 # return of calling type_get_default_fmt() with the variable's type
 2992 function var_get_fmt(var, _fmt) {
 2993         obj_assert_class(var, Var)
 2994 
 2995         # If defined on var, return it
 2996         if ((_fmt = get(var, p_fmt)) != null)
 2997                 return (_fmt)
 2998 
 2999         # Fall back on the type's default
 3000         return (type_get_default_fmt(get(var, p_type)))
 3001 }
 3002 
 3003 # Return a new MacroDefine instance for the given variable, macro type,
 3004 # and value
 3005 function var_get_macro(var, macro_type, value, _macro) {
 3006         obj_assert_class(var, Var)
 3007         obj_assert_class(macro_type, MacroType)
 3008 
 3009         return (macro_new(var_get_macro_name(var, macro_type), value))
 3010 }
 3011 
 3012 # Return the preprocessor constant name to be used with `var` for the given
 3013 # macro_type
 3014 function var_get_macro_name(var, macro_type, _var_name, _suffix) {
 3015         obj_assert_class(var, Var)
 3016         obj_assert_class(macro_type, MacroType)
 3017 
 3018         _var_name = get(var, p_name)
 3019         _suffix = get(macro_type, p_const_suffix)
 3020 
 3021         return("BHND_NVAR_" toupper(_var_name) _suffix)
 3022 }
 3023 
 3024 # Create a new SromLayout instance
 3025 function srom_layout_new(rev_desc, _obj)
 3026 {
 3027         _obj = obj_new(SromLayout)
 3028         set(_obj, p_revisions, rev_desc)
 3029         set(_obj, p_entries, array_new())
 3030         set(_obj, p_revmap, map_new())
 3031         set(_obj, p_output_var_counts, map_new())
 3032         set(_obj, p_line, NR)
 3033 
 3034         return (_obj)
 3035 }
 3036 
 3037 # Register a new entry with the srom layout
 3038 function srom_layout_add_entry(layout, entry, _revmap, _name, _rev_start,
 3039     _rev_end, _var, _prev_entry, _count, _i)
 3040 {
 3041         obj_assert_class(layout, SromLayout)
 3042         obj_assert_class(entry, SromEntry)
 3043 
 3044         _layout_revmap = get(layout, p_revmap)
 3045         _layout_var_count = get(layout, p_output_var_counts)
 3046 
 3047         _var = get(entry, p_var)
 3048         _name = get(_var, p_name)
 3049 
 3050         # Add to revision array
 3051         array_append(get(layout, p_entries), entry)
 3052 
 3053         # Add to the revision map tables
 3054         _rev_start = get(get(entry, p_revisions), p_start)
 3055         _rev_end = get(get(entry, p_revisions), p_end)
 3056 
 3057         for (_i = _rev_start; _i <= _rev_end; _i++) {
 3058                 # Check for existing entry
 3059                 _prev_entry = srom_layout_find_entry(layout, _name, _i)
 3060                 if (_prev_entry != null) {
 3061                         error("redefinition of variable '" _name "' for SROM " \
 3062                             "revision " _i " (previously defined on line " \
 3063                             get(_prev_entry, p_line) ")")
 3064                 }
 3065 
 3066                 # Add to the (varname,revision) map
 3067                 map_set(_layout_revmap, (_name SUBSEP _i), entry)
 3068 
 3069                 # If an output variable, set or increment the output variable
 3070                 # count
 3071                 if (!srom_entry_should_output(entry, _i))
 3072                         continue
 3073 
 3074                 if (!map_contains(_layout_var_count, _i)) {
 3075                         map_set(_layout_var_count, _i, 1)
 3076                 } else {
 3077                         _count = map_get(_layout_var_count, _i)
 3078                         map_set(_layout_var_count, _i, _count + 1)
 3079                 }
 3080         }
 3081 }
 3082 
 3083 
 3084 # Return the variable name to be used when emitting C declarations
 3085 # for this SROM layout
 3086 #
 3087 # The name is gauranteed to be unique across SROM layouts with non-overlapping
 3088 # revision ranges
 3089 function srom_layout_get_variable_name(layout, _revs) {
 3090         obj_assert_class(layout, SromLayout)
 3091 
 3092         _revs = get(layout, p_revisions)
 3093 
 3094         return ("bhnd_sprom_layout_r" get(_revs, p_start) \
 3095             "_r" get(_revs, p_end))
 3096 }
 3097 
 3098 # Return true if the given SROM revision is defined by the layout, false
 3099 # otherwise
 3100 function srom_layout_has_rev(layout, rev) {
 3101         obj_assert_class(layout, SromLayout)
 3102         return (revrange_contains(get(layout, p_revisions), rev))
 3103 }
 3104 
 3105 
 3106 # Return the total number of output variables (variables to be included
 3107 # in the SROM layout bindings) for the given SROM revision
 3108 function srom_layout_num_output_vars(layout, rev, _counts)
 3109 {
 3110         obj_assert_class(layout, SromLayout)
 3111 
 3112         _counts = get(layout, p_output_var_counts)
 3113         if (!map_contains(_counts, rev))
 3114                 return (0)
 3115 
 3116         return (map_get(_counts, rev))
 3117 }
 3118 
 3119 # Return the SromEntry defined for the given variable name and SROM revision,
 3120 # or null if none
 3121 function srom_layout_find_entry(layout, vname, revision, _key, _srom_revmap) {
 3122         obj_assert_class(layout, SromLayout)
 3123 
 3124         _srom_revmap = get(layout, p_revmap)
 3125 
 3126         # SromEntry are mapped by name,revision composite keys
 3127         _key = vname SUBSEP revision
 3128         if (!map_contains(_srom_revmap, _key))
 3129                 return (null)
 3130 
 3131         return (map_get(_srom_revmap, _key))
 3132         
 3133 }
 3134 
 3135 # Create a new SromLayoutFilter instance, checking that `revs`
 3136 # falls within the parent's revision range
 3137 function srom_layout_filter_new(parent, revs, _obj, _start, _end, _parent_revs) {
 3138         obj_assert_class(parent, SromLayout)
 3139         obj_assert_class(revs, RevRange)
 3140 
 3141         # Fetch our parent's revision range, confirm that we're
 3142         # a strict subset
 3143         _start = get(revs, p_start)
 3144         _end = get(revs, p_end)
 3145         _parent_revs = get(parent, p_revisions)
 3146 
 3147         if (!revrange_contains(_parent_revs, _start))
 3148                 error("'" _start "' is outside of parent range")
 3149 
 3150         if (!revrange_contains(_parent_revs, _end))
 3151                 error("'" _end "' is outside of parent range")
 3152 
 3153         if (revrange_equal(revs, _parent_revs)) {
 3154                 error("srom range '" revrange_to_string(revs) "' is " \
 3155                     "identical to parent range of '" \
 3156                         revrange_to_string(_parent_revs) "'")
 3157         }
 3158 
 3159         # Construct and return new filter instance
 3160         _obj = obj_new(SromLayoutFilter)
 3161         set(_obj, p_parent, parent)
 3162         set(_obj, p_revisions, revs)
 3163         set(_obj, p_line, NR)
 3164 
 3165         return (_obj)
 3166 }
 3167 
 3168 #
 3169 # Create a new SromEntry instance
 3170 #
 3171 # var:          The variable referenced by this entry
 3172 # revisions:    The SROM revisions to which this entry applies
 3173 # base_offset:  The SROM entry offset; any relative segment offsets will be
 3174 #               calculated relative to the base offset
 3175 # type:         The SROM's value type; this may be a subtype of the variable
 3176 #               type, and defines the data (width, sign, etc) to be read from
 3177 #               SROM.
 3178 # 
 3179 function srom_entry_new(var, revisions, base_offset, type, _obj) {
 3180         obj_assert_class(var, Var)
 3181         if (revisions != null)
 3182                 obj_assert_class(revisions, RevRange)
 3183 
 3184         _obj = obj_new(SromEntry)
 3185         set(_obj, p_var, var)
 3186         set(_obj, p_revisions, revisions)
 3187         set(_obj, p_base_offset, base_offset)
 3188         set(_obj, p_type, type)
 3189         set(_obj, p_offsets, array_new())
 3190         set(_obj, p_line, NR)
 3191 
 3192         return (_obj)
 3193 }
 3194 
 3195 # Return true if the SromEntry has an array type
 3196 function srom_entry_has_array_type(entry) {
 3197         obj_assert_class(entry, SromEntry)
 3198 
 3199         return (obj_is_instanceof(get(entry, p_type), ArrayType))
 3200 }
 3201 
 3202 # Return the number of array elements defined by this SromEntry's type,
 3203 # or 1 if the entry does not have an array type.
 3204 function srom_entry_get_array_len(entry, _type) {
 3205         obj_assert_class(entry, SromEntry)
 3206 
 3207         return (type_get_nelem(get(entry, p_type)))
 3208 }
 3209 
 3210 #
 3211 # Return true if the given entry should be included in the output bindings
 3212 # generated for the given revision, false otherwise.
 3213 #
 3214 function srom_entry_should_output(entry, rev, _var, _revs)
 3215 {
 3216         obj_assert_class(entry, SromEntry)
 3217 
 3218         _var = get(entry, p_var)
 3219         _revs = get(entry, p_revisions)
 3220 
 3221         # Exclude internal variables
 3222         if (var_is_internal(_var))
 3223                 return (0)
 3224 
 3225         # Exclude inapplicable entry revisions
 3226         if (!revrange_contains(_revs, rev))
 3227                 return (0)
 3228 
 3229         return (1)
 3230 }
 3231 
 3232 #
 3233 # Return the single, non-shifted, non-masked offset/segment for the given
 3234 # SromEntry, or throw an error if the entry contains multiple offsets/segments.
 3235 #
 3236 # This is used to fetch special-cased variable definitions that are required
 3237 # to present a single simple offset.
 3238 #
 3239 function srom_entry_get_single_segment(entry, _offsets, _segments, _seg,
 3240     _base_type, _default_mask)
 3241 {
 3242         obj_assert_class(entry, SromEntry)
 3243 
 3244         # Fetch the single offset's segment list
 3245         _offsets = get(entry, p_offsets)
 3246         if (array_size(_offsets) != 1)
 3247                 errorc(get(entry, p_line), "unsupported offset count")
 3248 
 3249         _segments = get(array_first(_offsets), p_segments)
 3250         if (array_size(_segments) != 1)
 3251                 errorc(get(entry, p_line), "unsupported segment count")
 3252 
 3253         # Fetch the single segment
 3254         _seg = array_first(_segments)
 3255         _base_type = srom_segment_get_base_type(_seg)
 3256         _default_mask = get(_base_type, p_mask)
 3257 
 3258         # Must not be shifted/masked
 3259         if (get(_seg, p_shift) != 0)
 3260                 errorc(obj_get_prop_nr(_seg, p_mask), "shift unsupported")
 3261 
 3262         if (get(_seg, p_mask) != _default_mask)
 3263                 errorc(obj_get_prop_nr(_seg, p_mask), "mask unsupported")       
 3264 
 3265         return  (_seg)
 3266 }
 3267 
 3268 # Create a new SromOffset instance
 3269 function srom_offset_new(_obj) {
 3270         _obj = obj_new(SromOffset)
 3271         set(_obj, p_segments, array_new())
 3272         set(_obj, p_line, NR)
 3273 
 3274         return (_obj)
 3275 }
 3276 
 3277 # Return the number of SromSegment instances defined by this offset.
 3278 function srom_offset_segment_count(offset) {
 3279         obj_assert_class(offset, SromOffset)
 3280         return (array_size(get(offset, p_segments)))
 3281 }
 3282 
 3283 # Return the idx'th segment. Will throw an error if idx falls outside
 3284 # the number of available segments.
 3285 function srom_offset_get_segment(offset, idx, _segments, _seg) {
 3286         obj_assert_class(offset, SromOffset)
 3287 
 3288         return (array_get(get(offset, p_segments), idx))
 3289 }
 3290 
 3291 # Create a new SromSegment instance
 3292 function srom_segment_new(offset, type, mask, shift, value, _obj) {
 3293         _obj = obj_new(SromSegment)
 3294         set(_obj, p_offset, offset)
 3295         set(_obj, p_type, type)
 3296         set(_obj, p_mask, mask)
 3297         set(_obj, p_shift, shift)
 3298         set(_obj, p_value, value)
 3299         set(_obj, p_line, NR)
 3300 
 3301         return (_obj)
 3302 }
 3303 
 3304 # Return true if the segment has an array type
 3305 function srom_segment_has_array_type(seg, _type) {
 3306         _type = srom_segment_get_type(seg)
 3307         return (obj_is_instanceof(_type, ArrayType))
 3308 }
 3309 
 3310 # Return the array count of the segment, or 1 if the segment does not have
 3311 # an array type 
 3312 function srom_segment_get_array_len(seg, _type) {
 3313         if (!srom_segment_has_array_type(seg))
 3314                 return (1)
 3315 
 3316         _type = srom_segment_get_type(seg)
 3317         return (get(_type, p_count))
 3318 }
 3319 
 3320 # Return the type of the segment
 3321 function srom_segment_get_type(seg) {
 3322         obj_assert_class(seg, SromSegment)
 3323         return (get(seg, p_type))
 3324 
 3325 }
 3326 
 3327 # Return the base type of the segment
 3328 function srom_segment_get_base_type(seg) {
 3329         return (type_get_base(srom_segment_get_type(seg)))
 3330 }
 3331 
 3332 # Return true if the two segments have identical types and attributes (i.e.
 3333 # differing only by offset)
 3334 function srom_segment_attributes_equal(lhs, rhs) {
 3335         obj_assert_class(lhs, SromSegment)
 3336         obj_assert_class(rhs, SromSegment)
 3337 
 3338         # type
 3339         if (!type_equal(get(lhs, p_type), get(rhs, p_type)))
 3340                 return (0)
 3341 
 3342         # mask
 3343         if (get(lhs, p_mask) != get(rhs, p_mask))
 3344                 return (0)
 3345 
 3346         # shift
 3347         if (get(lhs, p_shift) != get(rhs, p_shift))
 3348                 return (0)
 3349 
 3350         # value
 3351         if (get(lhs, p_value) != get(rhs, p_value))
 3352                 return (0)
 3353 
 3354         return (1)
 3355 }
 3356 
 3357 # Return a human-readable representation of a Segment instance
 3358 function segment_to_string(seg, _str, _t, _m, _s,  _attrs, _attr_str) {
 3359         _attrs = array_new()
 3360 
 3361         # include type (if specified)
 3362         if ((_t = get(seg, p_type)) != null)
 3363                 _str = (type_to_string(_t) " ")
 3364 
 3365         # include offset
 3366         _str = (_str sprintf("0x%X", get(seg, p_offset)))
 3367 
 3368         # append list of attributes
 3369         if ((_m = get(seg, p_mask)) != null)
 3370                 array_append(_attrs, ("&" _m))
 3371 
 3372         if ((_s = get(seg, p_shift)) != null) {
 3373                 if (_s > 0)
 3374                         _s = ">>" _s
 3375                 else
 3376                         _s = "<<" _s
 3377                 array_append(_attrs, _s)
 3378         }
 3379 
 3380         _attr_str = array_join(_attrs, ", ")
 3381         obj_delete(_attrs)
 3382 
 3383         if (_attr_str == "")
 3384                 return (_str)
 3385         else
 3386                 return (_str " (" _attr_str ")")
 3387 }
 3388 
 3389 # return the flag definition for variable `v`
 3390 function gen_var_flags(v, _type, _flags, _flag, _str)
 3391 {
 3392         _num_flags = 0;
 3393         _type = get(v, p_type)
 3394         _flags = array_new()
 3395 
 3396         # VF_PRIVATE
 3397         if (get(v, p_access) == VAccessPrivate)
 3398                 array_append(_flags, VFlagPrivate)
 3399 
 3400         # VF_IGNALL1
 3401         if (get(v, p_ignall1))
 3402                 array_append(_flags, VFlagIgnoreAll1)
 3403 
 3404         # If empty, return empty flag value
 3405         if (array_size(_flags) == 0) {
 3406                 obj_delete(_flags)
 3407                 return ("0")
 3408         }
 3409 
 3410         # Join all flag constants with |
 3411         _str = array_join(_flags, "|", class_get_prop_id(VFlag, p_const))
 3412 
 3413         # Clean up
 3414         obj_delete(_flags)
 3415 
 3416         return (_str)
 3417 }
 3418 
 3419 #
 3420 # Return the absolute value
 3421 #
 3422 function abs(i) {
 3423         return (i < 0 ? -i : i)
 3424 }
 3425 
 3426 #
 3427 # Return the minimum of two values
 3428 #
 3429 function min(lhs, rhs) {
 3430         return (lhs < rhs ? lhs : rhs)
 3431 }
 3432 
 3433 #
 3434 # Return the maximum of two values
 3435 #
 3436 function max(lhs, rhs) {
 3437         return (lhs > rhs ? lhs : rhs)
 3438 }
 3439 
 3440 #
 3441 # Parse a hex string
 3442 #
 3443 function parse_hex_string(str, _hex_pstate, _out, _p, _count) {
 3444         if (!AWK_REQ_HEX_PARSING)
 3445                 return (str + 0)
 3446 
 3447         # Populate hex parsing lookup table on-demand
 3448         if (!("F" in _g_hex_table)) {
 3449                 for (_p = 0; _p < 16; _p++) {
 3450                         _g_hex_table[sprintf("%X", _p)] = _p
 3451                         _g_hex_table[sprintf("%x", _p)] = _p
 3452                 }
 3453         }
 3454 
 3455         # Split input into an array
 3456         _count = split(toupper(str), _hex_pstate, "")
 3457         _p = 1
 3458 
 3459         # Skip leading '0x'
 3460         if (_count >= 2 && _hex_pstate[1] == "0") {
 3461                 if (_hex_pstate[2] == "x" || _hex_pstate[2] == "X")
 3462                         _p += 2
 3463         }
 3464 
 3465         # Parse the hex_digits
 3466         _out = 0
 3467         for (; _p <= _count; _p++)
 3468                 _out = (_out * 16) + _g_hex_table[_hex_pstate[_p]]
 3469 
 3470         return (_out)
 3471 }
 3472 
 3473 #
 3474 # Return the integer representation of an unsigned decimal, hexadecimal, or
 3475 # octal string
 3476 #
 3477 function parse_uint_string(str) {
 3478         if (str ~ UINT_REGEX)
 3479                 return (int(str))
 3480         else if (str ~ HEX_REGEX)
 3481                 return (parse_hex_string(str))
 3482         else
 3483                 error("invalid integer value: '" str "'")
 3484 }
 3485 
 3486 #
 3487 # Parse an offset string, stripping any leading '+' or trailing ':' or ','
 3488 # characters
 3489 #
 3490 # +0x0:
 3491 # 0x0,
 3492 # ...
 3493 #
 3494 function parse_uint_offset(str) {
 3495         # Drop any leading '+'
 3496         sub(/^\+/, "", str)
 3497 
 3498         # Drop any trailing ':', ',', or '|'
 3499         sub("[,|:]$", "", str)
 3500 
 3501         # Parse the cleaned up string
 3502         return (parse_uint_string(str))
 3503 }
 3504 
 3505 #
 3506 # Print msg to output file, without indentation
 3507 #
 3508 function emit_ni(msg) {
 3509         printf("%s", msg) >> OUTPUT_FILE
 3510 }
 3511 
 3512 #
 3513 # Print msg to output file, indented for the current `output_depth`
 3514 #
 3515 function emit(msg, _ind) {
 3516         for (_ind = 0; _ind < output_depth; _ind++)
 3517                 emit_ni("\t")
 3518 
 3519         emit_ni(msg)
 3520 }
 3521 
 3522 #
 3523 # Print a warning to stderr
 3524 #
 3525 function warn(msg) {
 3526         print "warning:", msg, "at", FILENAME, "line", NR > "/dev/stderr"
 3527 }
 3528 
 3529 #
 3530 # Print an warning message without including the source line information
 3531 #
 3532 function warnx(msg) {
 3533         print "warning:", msg > "/dev/stderr"
 3534 }
 3535 
 3536 #
 3537 # Print a compiler error to stderr with a caller supplied
 3538 # line number
 3539 #
 3540 function errorc(line, msg) {
 3541         errorx(msg " at " FILENAME " line " line)
 3542 }
 3543 
 3544 #
 3545 # Print a compiler error to stderr
 3546 #
 3547 function error(msg) {
 3548         errorx(msg " at " FILENAME " line " NR ":\n\t" $0)
 3549 }
 3550 
 3551 #
 3552 # Print an error message without including the source line information
 3553 #
 3554 function errorx(msg) {
 3555         print "error:", msg > "/dev/stderr"
 3556         _EARLY_EXIT=1
 3557         exit 1
 3558 }
 3559 
 3560 #
 3561 # Print a debug output message
 3562 #
 3563 function debug(msg, _i) {
 3564         if (!DEBUG)
 3565                 return
 3566         for (_i = 1; _i < _g_parse_stack_depth; _i++)
 3567                 printf("\t") > "/dev/stderr"
 3568         print msg > "/dev/stderr"
 3569 }
 3570 
 3571 #
 3572 # Advance to the next non-comment input record
 3573 #
 3574 function next_line(_result) {
 3575         do {
 3576                 _result = getline
 3577         } while (_result > 0 && $0 ~ /^[ \t]*#.*/) # skip comment lines
 3578         return (_result)
 3579 }
 3580 
 3581 #
 3582 # Advance to the next input record and verify that it matches @p regex
 3583 #
 3584 function getline_matching(regex, _result) {
 3585         _result = next_line()
 3586         if (_result <= 0)
 3587                 return (_result)
 3588 
 3589         if ($0 ~ regex)
 3590                 return (1)
 3591 
 3592         return (-1)
 3593 }
 3594 
 3595 #
 3596 # Shift the current fields left by `n`.
 3597 #
 3598 # If all fields are consumed and the optional do_getline argument is true,
 3599 # read the next line.
 3600 #
 3601 function shiftf(n, do_getline, _i) {
 3602         if (n > NF)
 3603                 error("shift past end of line")
 3604 
 3605         if (n == NF) {
 3606                 # If shifting the entire line, just reset the line value
 3607                 $0 = ""
 3608         } else {
 3609                 for (_i = 1; _i <= NF-n; _i++) {
 3610                         $(_i) = $(_i+n)
 3611                 }
 3612                 NF = NF - n
 3613         }
 3614 
 3615         if (NF == 0 && do_getline)
 3616                 next_line()
 3617 }
 3618 
 3619 # Push a new parser state.
 3620 function parser_state_push(ctx, is_block, _state) {
 3621         _state = obj_new(ParseState)
 3622         set(_state, p_ctx, ctx)
 3623         set(_state, p_is_block, is_block)
 3624         set(_state, p_line, NR)
 3625 
 3626         _g_parse_stack_depth++
 3627         _g_parse_stack[_g_parse_stack_depth] = _state
 3628 }
 3629 
 3630 # Fetch the current parser state
 3631 function parser_state_get() {
 3632         if (_g_parse_stack_depth == 0)
 3633                 errorx("parser_state_get() called with empty parse stack")
 3634 
 3635         return (_g_parse_stack[_g_parse_stack_depth])
 3636 }
 3637 
 3638 # Pop the current parser state
 3639 function parser_state_pop(_block_state, _closes_block) {
 3640         if (_g_parse_stack_depth == 0)
 3641                 errorx("parser_state_pop() called with empty parse stack")
 3642 
 3643         _closes_block = get(parser_state_get(), p_is_block)
 3644 
 3645         delete _g_parse_stack[_g_parse_stack_depth]
 3646         _g_parse_stack_depth--
 3647 
 3648         if (_closes_block)
 3649                 debug("}")
 3650 }
 3651 
 3652 # Fetch the current context object associated with this parser state
 3653 # The object will be asserted as being an instance of the given class.
 3654 function parser_state_get_context(class, _ctx_obj) {
 3655         _ctx_obj = get(parser_state_get(), p_ctx)
 3656         obj_assert_class(_ctx_obj, class)
 3657 
 3658         return (_ctx_obj)
 3659 }
 3660 
 3661 # Walk the parser state stack until a context object of the given class
 3662 # is found. If the top of the stack is reached without finding a context object
 3663 # of the requested type, an error will be thrown.
 3664 function parser_state_find_context(class, _state, _ctx, _i) {
 3665         if (class == null)
 3666                 errorx("parser_state_find_context() called with null class")
 3667 
 3668         # Find the first context instance inheriting from `class`
 3669         for (_i = 0; _i < _g_parse_stack_depth; _i++) {
 3670                 _state = _g_parse_stack[_g_parse_stack_depth - _i]
 3671                 _ctx = get(_state, p_ctx)
 3672 
 3673                 # Check for match
 3674                 if (obj_is_instanceof(_ctx, class))
 3675                         return (_ctx)
 3676         }
 3677 
 3678         # Not found
 3679         errorx("no context instance of type '" class_get_name(class) "' " \
 3680             "found in parse stack")
 3681 }
 3682 
 3683 #
 3684 # Find opening brace and push a new parser state for a brace-delimited block.
 3685 #
 3686 function parser_state_open_block(ctx) {
 3687         if ($0 ~ "{" || getline_matching("^[ \t]*{") > 0) {
 3688                 parser_state_push(ctx, 1)
 3689                 sub("^[^{]*{", "", $0)
 3690                 return
 3691         }
 3692 
 3693         error("found '"$1 "' instead of expected '{'")
 3694 }
 3695 
 3696 #
 3697 # Find closing brace and pop parser states until the first
 3698 # brace-delimited block is discarded.
 3699 #
 3700 function parser_state_close_block(_next_state, _found_block) {
 3701         if ($0 !~ "}")
 3702                 error("internal error - no closing brace")
 3703 
 3704         # pop states until we exit the first enclosing block
 3705         do {
 3706                 _next_state = parser_state_get()
 3707                 _found_block = get(_next_state, p_is_block)
 3708                 parser_state_pop()
 3709         } while (!_found_block)
 3710 
 3711         # strip everything prior to the block closure
 3712         sub("^[^}]*}", "", $0)
 3713 }
 3714 
 3715 # Evaluates to true if the current parser state is defined with a context of
 3716 # the given class
 3717 function in_parser_context(class, _ctx) {
 3718         if (class == null)
 3719                 errorx("called in_parser_context() with null class")
 3720 
 3721         _ctx = get(parser_state_get(), p_ctx)
 3722         return (obj_is_instanceof(_ctx, class))
 3723 }
 3724 
 3725 #
 3726 # Parse and return a revision range from the current line.
 3727 #
 3728 # 4
 3729 # 4-10  # revisions 4-10, inclusive
 3730 # > 4
 3731 # < 4
 3732 # >= 4
 3733 # <= 4
 3734 #
 3735 function parse_revrange(_start, _end, _robj) {
 3736         _start = 0
 3737         _end = 0
 3738 
 3739         if ($2 ~ "[0-9]*-[0-9*]") {
 3740                 split($2, _g_rev_range, "[ \t]*-[ \t]*")
 3741                 _start = int(_g_rev_range[1])
 3742                 _end = int(_g_rev_range[2])
 3743         } else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") {
 3744                 if ($2 == ">") {
 3745                         _start = int($3)+1
 3746                         _end = REV_MAX
 3747                 } else if ($2 == ">=") {
 3748                         _start = int($3)
 3749                         _end = REV_MAX
 3750                 } else if ($2 == "<" && int($3) > 0) {
 3751                         _start = 0
 3752                         _end = int($3)-1
 3753                 } else if ($2 == "<=") {
 3754                         _start = 0
 3755                         _end = int($3)-1
 3756                 } else {
 3757                         error("invalid revision descriptor")
 3758                 }
 3759         } else if ($2 ~ "[1-9][0-9]*") {
 3760                 _start = int($2)
 3761                 _end = int($2)
 3762         } else {
 3763                 error("invalid revision descriptor")
 3764         }
 3765 
 3766         return (revrange_new(_start, _end))
 3767 }
 3768 
 3769 # 
 3770 # Parse a variable group block starting at the current line
 3771 #
 3772 # group "Group Name" {
 3773 #       u8      var_name[10] {
 3774 #               ...
 3775 #       }
 3776 #       ...
 3777 # }
 3778 #
 3779 function parse_variable_group(_ctx, _groups, _group, _group_name) {
 3780         _ctx = parser_state_get_context(NVRAM)
 3781 
 3782         # Seek to the start of the name string
 3783         shiftf(1)
 3784 
 3785         # Parse the first line
 3786         _group_name = stringconstant_parse_line($0)
 3787 
 3788         # Incrementally parse line continuations
 3789         while (get(_group_name, p_continued)) {
 3790                 getline
 3791                 stringconstant_append_line(_group_name, $0)
 3792         }
 3793 
 3794         debug("group \"" get(_group_name, p_value) "\" {")
 3795 
 3796         # Register the new variable group
 3797         _groups = get(_ctx, p_var_groups)
 3798         _group = var_group_new(_group_name)
 3799         array_append(_groups, _group)
 3800 
 3801         # Push our variable group block
 3802         parser_state_open_block(_group)
 3803 }
 3804 
 3805 
 3806 #
 3807 # Parse a variable definition block starting at the current line
 3808 #
 3809 # u8    var_name[10] {
 3810 #       all1    ignore
 3811 #       desc    ...
 3812 # }
 3813 #
 3814 function parse_variable_defn(_ctx, _vaccess, _type, _name, _fmt, _var,
 3815     _var_list)
 3816 {
 3817         _ctx = parser_state_get_context(SymbolContext)
 3818 
 3819         # Check for access modifier
 3820         if ($1 == "private") {
 3821                 _vaccess = VAccessPrivate
 3822                 shiftf(1)
 3823         } else if ($1 == "internal") {
 3824                 _vaccess = VAccessInternal
 3825                 shiftf(1)
 3826         } else {
 3827                 _vaccess = VAccessPublic
 3828         }
 3829 
 3830         # Find the base type
 3831         if ((_type = type_named($1)) == null)
 3832                 error("unknown type '" $1 "'")
 3833 
 3834         # Parse (and trim) any array specifier from the variable name
 3835         _name = $2
 3836         _type = parse_array_type_specifier(_name, _type)
 3837         sub(ARRAY_REGEX"$", "", _name)
 3838 
 3839         # Look for an existing variable definition
 3840         if (_name in _g_var_names) {
 3841                 error("variable identifier '" _name "' previously defined at " \
 3842                     "line " get(_g_var_names[_name], p_line))
 3843         }
 3844 
 3845         # Construct new variable instance
 3846         _var = var_new(_vaccess, _name, _type)
 3847         debug((_private ? "private " : "") type_to_string(_type) " " _name " {")
 3848 
 3849         # Register in global name table
 3850         _g_var_names[_name] = _var
 3851 
 3852         # Add to our parent context
 3853         _var_list = get(_ctx, p_vars)
 3854         array_append(_var_list, _var)
 3855 
 3856         # Push our variable definition block
 3857         parser_state_open_block(_var)
 3858 }
 3859 
 3860 
 3861 #
 3862 # Return a string containing the human-readable list of valid Fmt names
 3863 #
 3864 function fmt_get_human_readable_list(_result, _fmts, _fmt, _nfmts, _i)
 3865 {
 3866         # Build up a string listing the valid formats
 3867         _fmts = map_to_array(ValueFormats)
 3868         _result = ""
 3869 
 3870         _nfmts = array_size(_fmts)
 3871         for (_i = 0; _i < _nfmts; _i++) {
 3872                 _fmt = array_get(_fmts, _i)
 3873                 if (_i+1 == _nfmts)
 3874                         _result = _result "or "
 3875 
 3876                 _result = _name_str \
 3877                     "'" get(_fmt, p_name) "'"
 3878 
 3879                 if (_i+1 < _nfmts)
 3880                         _result = _result ", "
 3881         }
 3882 
 3883         obj_delete(_fmts)
 3884         return (_result)
 3885 }
 3886 
 3887 #
 3888 # Parse a variable parameter from the current line
 3889 #
 3890 # fmt   (decimal|hex|macaddr|...)
 3891 # all1  ignore
 3892 # desc  "quoted string"
 3893 # help  "quoted string"
 3894 #
 3895 function parse_variable_param(param_name, _var, _vprops, _prop_id, _pval) {
 3896         _var = parser_state_get_context(Var)
 3897 
 3898         if (param_name == "fmt") {
 3899                 debug($1 " " $2)
 3900 
 3901                 # Check for an existing definition
 3902                 if ((_pval = get(_var, p_fmt)) != null) {
 3903                         error("fmt previously specified on line " \
 3904                             obj_get_prop_nr(_var, p_fmt))
 3905                 }
 3906 
 3907                 # Validate arguments
 3908                 if (NF != 2) {
 3909                         error("'" $1 "' requires a single parameter value of " \
 3910                             fmt_get_human_readable_list())
 3911                 }
 3912 
 3913                 if ((_pval = fmt_named($2)) == null) {
 3914                         error("'" $1 "' value '" $2 "' unrecognized. Must be " \
 3915                             "one of " fmt_get_human_readable_list())
 3916                 }
 3917 
 3918                 # Set fmt reference
 3919                 set(_var, p_fmt, _pval)
 3920         } else if (param_name == "all1") {
 3921                 debug($1 " " $2)
 3922                 
 3923                 # Check for an existing definition
 3924                 if ((_pval = get(_var, p_ignall1)) != null) {
 3925                         error("all1 previously specified on line " \
 3926                             obj_get_prop_nr(_var, p_ignall1))
 3927                 }
 3928 
 3929                 # Check argument
 3930                 if (NF != 2)
 3931                         error("'" $1 "'requires a single 'ignore' argument")
 3932                 else if ($2 != "ignore")
 3933                         error("unknown "$1" value '"$2"', expected 'ignore'")
 3934 
 3935                 # Set variable property
 3936                 set(_var, p_ignall1, 1)
 3937         } else if (param_name == "desc" || param_name == "help") {
 3938                 # Fetch an indirect property reference for either the 'desc'
 3939                 # or 'help' property
 3940                 _prop_id = obj_get_named_prop_id(_var, param_name)
 3941 
 3942                 # Check for an existing definition
 3943                 if ((_pval = prop_get(_var, _prop_id)) != null) {
 3944                         error(get(_var, p_name) " '" $1 "' redefined " \
 3945                             "(previously defined on line " \
 3946                             obj_get_prop_id_nr(_var, _prop_id) ")")
 3947                 }
 3948 
 3949                 # Seek to the start of the desc/help string
 3950                 shiftf(1)
 3951 
 3952                 # Parse the first line
 3953                 _pval = stringconstant_parse_line($0)
 3954 
 3955                 # Incrementally parse line continuations
 3956                 while (get(_pval, p_continued)) {
 3957                         getline
 3958                         stringconstant_append_line(_pval, $0)
 3959                 }
 3960 
 3961                 debug(param_name " \"" get(_pval, p_value) "\"")
 3962 
 3963                 # Add to the var object
 3964                 prop_set(_var, _prop_id, _pval)
 3965         } else {
 3966                 error("unknown variable property type: '" param_name "'")
 3967         }
 3968 }
 3969 
 3970 
 3971 #
 3972 # Parse a top-level SROM layout block starting at the current line
 3973 #
 3974 # srom 4-7 {
 3975 #     0x000: ...
 3976 # }
 3977 #
 3978 function parse_srom_layout(_nvram, _srom_layouts, _revs, _layout) {
 3979         _nvram = parser_state_get_context(NVRAM)
 3980         _srom_layouts = get(_nvram, p_srom_layouts)
 3981 
 3982         # Parse revision descriptor and register SROM
 3983         # instance
 3984         _revs = parse_revrange()
 3985         _layout = srom_layout_new(_revs)
 3986         nvram_add_srom_layout(_nvram, _layout)
 3987 
 3988         debug("srom " revrange_to_string(_revs) " {")
 3989 
 3990         # Push new SROM parser state
 3991         parser_state_open_block(_layout)
 3992 }
 3993 
 3994 
 3995 #
 3996 # Parse a nested srom range filter block starting at the current line
 3997 # srom 4-7 {
 3998 #       # Filter block
 3999 #       srom 5 {
 4000 #               0x000: ...
 4001 #       }
 4002 # }
 4003 #
 4004 function parse_srom_layout_filter(_parent, _revs, _filter) {
 4005         _parent = parser_state_get_context(SromLayout)
 4006 
 4007         # Parse revision descriptor
 4008         _revs = parse_revrange()
 4009 
 4010         # Construct the filter (which also validates the revision range)
 4011         _filter = srom_layout_filter_new(_parent, _revs)
 4012 
 4013         debug("srom " revrange_to_string(_revs) " {")
 4014 
 4015         # Push new SROM parser state
 4016         parser_state_open_block(_filter)        
 4017 }
 4018 
 4019 
 4020 #
 4021 # Parse a SROM offset segment's attribute list from the current line
 4022 #
 4023 # <empty line>
 4024 # (&0xF0, >>4, =0x5340)
 4025 # ()
 4026 #
 4027 # Attribute designators:
 4028 #       &0xF    Mask value with 0xF
 4029 #       <<4     Shift left 4 bits
 4030 #       >>4     Shift right 4 bits
 4031 #       =0x53   The parsed value must be equal to this constant value
 4032 #
 4033 # May be followed by a | indicating that this segment should be OR'd with the
 4034 # segment that follows, or a terminating , indicating that a new offset's
 4035 # list of segments may follow.
 4036 #
 4037 function parse_srom_segment_attributes(offset, type, _attrs, _num_attr, _attr,
 4038     _mask, _shift, _value, _i)
 4039 {
 4040         # seek to offset (attributes...) or end of the offset expr (|,)
 4041         sub("^[^,(|){}]+", "", $0)
 4042 
 4043         # defaults
 4044         _mask = type_get_default_mask(type)
 4045         _shift = 0
 4046 
 4047         # parse attributes
 4048         if ($1 ~ "^\\(") {
 4049                 # extract attribute list
 4050                 if (match($0, /\([^|\(\)]*\)/) <= 0)
 4051                         error("expected attribute list")
 4052 
 4053                 _attrs = substr($0, RSTART+1, RLENGTH-2)
 4054 
 4055                 # drop attribute list from the input line
 4056                 $0 = substr($0, RSTART+RLENGTH, length($0) - RSTART+RLENGTH)
 4057 
 4058                 # parse attributes
 4059                 _num_attr = split(_attrs, _g_attrs, ",[ \t]*")
 4060                 for (_i = 1; _i <= _num_attr; _i++) {
 4061                         _attr = _g_attrs[_i]
 4062         
 4063                         if (sub("^&[ \t]*", "", _attr) > 0) {
 4064                                 _mask = parse_uint_string(_attr)
 4065                         } else if (sub("^<<[ \t]*", "", _attr) > 0) {
 4066                                 _shift = - parse_uint_string(_attr)
 4067                         } else if (sub("^>>[ \t]*", "", _attr) > 0) {
 4068                                 _shift = parse_uint_string(_attr)
 4069                         } else if (sub("^=[ \t]*", "", _attr) > 0) {
 4070                                 _value = _attr
 4071                         } else {
 4072                                 error("unknown attribute '" _attr "'")
 4073                         }
 4074                 }
 4075         }
 4076 
 4077         return (srom_segment_new(offset, type, _mask, _shift, _value))
 4078 }
 4079 
 4080 #
 4081 # Parse a SROM offset's segment declaration from the current line
 4082 #
 4083 # +0x0: u8 (&0xF0, >>4)         # read 8 bits at +0x0 (relative to srom entry
 4084 #                               # offset, apply 0xF0 mask, shift >> 4
 4085 # 0x10: u8 (&0xF0, >>4)         # identical to above, but perform the read at
 4086 #                               # absolute offset 0x10
 4087 #
 4088 # +0x0: u8                      # no attributes
 4089 # 0x10: u8
 4090 #
 4091 # +0x0                          # simplified forms denoted by lack of ':'; the
 4092 # 0x0                           # type is inherited from the parent SromEntry
 4093 #
 4094 #
 4095 function parse_srom_segment(base_offset, base_type, _simple, _type, _type_str,
 4096     _offset, _attrs, _num_attr, _attr, _mask, _shift, _off_desc)
 4097 {
 4098         # Fetch the offset value
 4099         _offset = $1
 4100 
 4101         # Offset string must be one of:
 4102         #       simplified entry: <offset|+reloff>
 4103         #               Provides only the offset, with the type inherited
 4104         #               from the original variable definition
 4105         #       standard entry: <offset|+reloff>:
 4106         #               Provides the offset, followed by a type
 4107         #
 4108         # We differentiate the two by looking for (and simultaneously removing)
 4109         # the trailing ':'
 4110         if (!sub(/:$/, "", _offset))
 4111                 _simple = 1
 4112 
 4113         # The offset may either be absolute (e.g. 0x180) or relative (e.g.
 4114         # +0x01).
 4115         #
 4116         # If we find a relative offset definition, we must trim the leading '+'
 4117         # and then add the base offset
 4118         if (sub(/^\+/, "", _offset)) {
 4119                 _offset = base_offset + parse_uint_offset(_offset)
 4120         } else {
 4121                 
 4122                 _offset = parse_uint_offset(_offset)
 4123         }
 4124 
 4125         # If simplified form, use the base type of the SROM entry. Otherwise,
 4126         # we need to parse the type.
 4127         if (_simple) {
 4128                 _type = base_type
 4129         } else {
 4130                 _type_str = $2
 4131                 sub(/,$/, "", _type_str) # trim trailing ',', if any
 4132 
 4133                 if ((_type = parse_type_string(_type_str)) == null)
 4134                         error("unknown type '" _type_str "'")
 4135         }
 4136 
 4137         # Parse the trailing (... attributes ...), if any
 4138         return (parse_srom_segment_attributes(_offset, _type))
 4139 }
 4140 
 4141 #
 4142 # Parse a SROM variable entry from the current line
 4143 # <offset>: <type> <varname><array spec> ...
 4144 #
 4145 function parse_srom_variable_entry(_srom, _srom_revs, _rev_start, _rev_end,
 4146     _srom_entries, _srom_revmap, _prev_entry, _ctx, _base_offset, _name,
 4147     _stype, _var, _entry, _offset, _seg, _i)
 4148 {
 4149         # Fetch our parent context
 4150         _ctx = parser_state_get_context(SromContext)
 4151         _srom_revs = get(_ctx, p_revisions)
 4152         _rev_start = get(_srom_revs, p_start)
 4153         _rev_end = get(_srom_revs, p_end)
 4154 
 4155         # Locate our enclosing layout
 4156         _srom = parser_state_find_context(SromLayout)
 4157         _srom_entries = get(_srom, p_entries)
 4158         _srom_revmap = get(_srom, p_revmap)
 4159 
 4160         # Verify argument count
 4161         if (NF < 3) {
 4162                 error("unrecognized srom entry syntax; must specify at " \
 4163                     "least \"<offset>: <type> <variable name>\"")
 4164         }
 4165 
 4166         # Parse the base offset
 4167         _base_offset = parse_uint_offset($1)
 4168 
 4169         # Parse the base type
 4170         if ((_stype = type_named($2)) == null)
 4171                 error("unknown type '" $2 "'")
 4172 
 4173         # Parse (and trim) any array specifier from the variable name
 4174         _name = $3
 4175         _stype = parse_array_type_specifier(_name, _stype)
 4176         sub(ARRAY_REGEX"$", "", _name)
 4177 
 4178         # Locate the variable definition
 4179         if (!(_name in _g_var_names))
 4180                 error("no definition found for variable '" _name "'")
 4181         _var = _g_var_names[_name]
 4182 
 4183         # The SROM entry type must be a subtype of the variable's declared
 4184         # type
 4185         if (!type_can_represent(get(_var, p_type), _stype)) {
 4186                 error("'" type_to_string(_stype) "' SROM value cannot be " \
 4187                     "coerced to '" type_to_string(get(_var, p_type)) " " _name \
 4188                     "' variable")
 4189         }
 4190 
 4191         # Create and register our new offset entry
 4192         _entry = srom_entry_new(_var, _srom_revs, _base_offset, _stype)
 4193         srom_layout_add_entry(_srom, _entry)
 4194 
 4195         # Seek to either the block start ('{'), or the attributes to be
 4196         # used for a single offset/segment entry at `offset`
 4197         shiftf(3)
 4198 
 4199         # Using the block syntax? */
 4200         if ($1 == "{") {
 4201                 debug(sprintf("0x%03x: %s %s {", _base_offset,
 4202                     type_to_string(_stype), _name))
 4203                 parser_state_open_block(_entry)
 4204         } else {
 4205                 # Otherwise, we're using the simplified syntax -- create and
 4206                 # register our implicit SromOffset
 4207                 _offset = srom_offset_new()
 4208                 array_append(get(_entry, p_offsets), _offset)
 4209 
 4210                 # Parse and register simplified segment syntax
 4211                 _seg = parse_srom_segment_attributes(_base_offset, _stype)
 4212                 array_append(get(_offset, p_segments), _seg)
 4213 
 4214                 debug(sprintf("0x%03x: %s %s { %s }", _base_offset,
 4215                     type_to_string(_stype), _name, segment_to_string(_seg)))
 4216         }
 4217 }
 4218 
 4219 #
 4220 # Parse all SromSegment entry segments readable starting at the current line
 4221 #
 4222 # <offset|+reloff>[,|]?
 4223 # <offset|+reloff>: <type>[,|]?
 4224 # <offset|+reloff>: <type> (<attributes>)[,|]?
 4225 #
 4226 function parse_srom_entry_segments(_entry, _base_off, _base_type, _offs,
 4227     _offset, _segs, _seg, _more_seg, _more_vals)
 4228 {
 4229         _entry = parser_state_get_context(SromEntry)
 4230         _base_off = get(_entry, p_base_offset)
 4231         _offs = get(_entry, p_offsets)
 4232 
 4233         _base_type = get(_entry, p_type)
 4234         _base_type = type_get_base(_base_type)
 4235 
 4236         # Parse all offsets
 4237         do {
 4238                 # Create a SromOffset
 4239                 _offset = srom_offset_new()
 4240                 _segs = get(_offset, p_segments)
 4241 
 4242                 array_append(_offs, _offset)
 4243 
 4244                 # Parse all segments
 4245                 do {
 4246                         _seg = parse_srom_segment(_base_off, _base_type)
 4247                         array_append(_segs, _seg)
 4248 
 4249                         # Do more segments follow?
 4250                         _more_seg = ($1 == "|")
 4251                         if (_more_seg)
 4252                                 shiftf(1, 1)
 4253 
 4254                         if (_more_seg)
 4255                                 debug(segment_to_string(_seg) " |")
 4256                         else
 4257                                 debug(segment_to_string(_seg))
 4258                 } while (_more_seg)
 4259 
 4260                 # Do more offsets follow?
 4261                 _more_vals = ($1 == ",")
 4262                 if (_more_vals)
 4263                         shiftf(1, 1)
 4264         } while (_more_vals)
 4265 }

Cache object: a629bb8689508b145f9cd1b08c1fcba5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.