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