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