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