]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/tools/nvram_map_gen.awk
MFV r362565:
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / tools / nvram_map_gen.awk
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 }