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