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