xref: /freebsd/sys/dev/bhnd/tools/nvram_map_gen.awk (revision 031beb4e239bfce798af17f5fe8dba8bcaf13d99)
1e83ce340SAdrian Chadd#!/usr/bin/awk -f
2e83ce340SAdrian Chadd
3e83ce340SAdrian Chadd#-
4e83ce340SAdrian Chadd# Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
5e83ce340SAdrian Chadd# All rights reserved.
6e83ce340SAdrian Chadd#
7e83ce340SAdrian Chadd# Redistribution and use in source and binary forms, with or without
8e83ce340SAdrian Chadd# modification, are permitted provided that the following conditions
9e83ce340SAdrian Chadd# are met:
10e83ce340SAdrian Chadd# 1. Redistributions of source code must retain the above copyright
11e83ce340SAdrian Chadd#    notice, this list of conditions and the following disclaimer,
12e83ce340SAdrian Chadd#    without modification.
13e83ce340SAdrian Chadd# 2. Redistributions in binary form must reproduce at minimum a disclaimer
14e83ce340SAdrian Chadd#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15e83ce340SAdrian Chadd#    redistribution must be conditioned upon including a substantially
16e83ce340SAdrian Chadd#    similar Disclaimer requirement for further binary redistribution.
17e83ce340SAdrian Chadd#
18e83ce340SAdrian Chadd# NO WARRANTY
19e83ce340SAdrian Chadd# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20e83ce340SAdrian Chadd# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21e83ce340SAdrian Chadd# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22e83ce340SAdrian Chadd# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23e83ce340SAdrian Chadd# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24e83ce340SAdrian Chadd# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25e83ce340SAdrian Chadd# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26e83ce340SAdrian Chadd# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27e83ce340SAdrian Chadd# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28e83ce340SAdrian Chadd# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29e83ce340SAdrian Chadd# THE POSSIBILITY OF SUCH DAMAGES.
30e83ce340SAdrian Chadd#
31e83ce340SAdrian Chadd
3277cb4d3eSLandon J. FullerBEGIN	{ main() }
3377cb4d3eSLandon J. FullerEND	{ at_exit() }
3477cb4d3eSLandon J. Fuller
3577cb4d3eSLandon J. Fuller#
3677cb4d3eSLandon J. Fuller# Print usage
3777cb4d3eSLandon J. Fuller#
3877cb4d3eSLandon J. Fullerfunction usage() {
3977cb4d3eSLandon J. Fuller	print "usage: bhnd_nvram_map.awk <input map> [-hd] [-o output file]"
4077cb4d3eSLandon J. Fuller	_EARLY_EXIT = 1
4177cb4d3eSLandon J. Fuller	exit 1
4277cb4d3eSLandon J. Fuller}
4377cb4d3eSLandon J. Fuller
4477cb4d3eSLandon J. Fullerfunction main(_i) {
45e83ce340SAdrian Chadd	RS="\n"
46e83ce340SAdrian Chadd
47e83ce340SAdrian Chadd	OUTPUT_FILE = null
48e83ce340SAdrian Chadd
4977cb4d3eSLandon J. Fuller	# Probe awk implementation's hex digit handling
5077cb4d3eSLandon J. Fuller	if ("0xA" + 0 != 10) {
5177cb4d3eSLandon J. Fuller		AWK_REQ_HEX_PARSING=1
5277cb4d3eSLandon J. Fuller	}
5377cb4d3eSLandon J. Fuller
54e83ce340SAdrian Chadd	# Output type
55e83ce340SAdrian Chadd	OUT_T = null
56e83ce340SAdrian Chadd	OUT_T_HEADER = "HEADER"
57e83ce340SAdrian Chadd	OUT_T_DATA = "DATA"
58*4f4eb606SMatt Macy	VERBOSE = 0
59e83ce340SAdrian Chadd
6077cb4d3eSLandon J. Fuller	# Tab width to use when calculating output alignment
6177cb4d3eSLandon J. Fuller	TAB_WIDTH = 8
6277cb4d3eSLandon J. Fuller
63e83ce340SAdrian Chadd	# Enable debug output
64e83ce340SAdrian Chadd	DEBUG = 0
65e83ce340SAdrian Chadd
66e83ce340SAdrian Chadd	# Maximum revision
6777cb4d3eSLandon J. Fuller	REV_MAX = 256
68e83ce340SAdrian Chadd
69e83ce340SAdrian Chadd	# Parse arguments
70e83ce340SAdrian Chadd	if (ARGC < 2)
71e83ce340SAdrian Chadd		usage()
72e83ce340SAdrian Chadd
7377cb4d3eSLandon J. Fuller	for (_i = 1; _i < ARGC; _i++) {
7477cb4d3eSLandon J. Fuller		if (ARGV[_i] == "--debug") {
75e83ce340SAdrian Chadd			DEBUG = 1
7677cb4d3eSLandon J. Fuller		} else if (ARGV[_i] == "-d" && OUT_T == null) {
77e83ce340SAdrian Chadd			OUT_T = OUT_T_DATA
7877cb4d3eSLandon J. Fuller		} else if (ARGV[_i] == "-h" && OUT_T == null) {
79e83ce340SAdrian Chadd			OUT_T = OUT_T_HEADER
80*4f4eb606SMatt Macy		} else if (ARGV[_i] == "-v") {
81*4f4eb606SMatt Macy		        VERBOSE = 1
8277cb4d3eSLandon J. Fuller		} else if (ARGV[_i] == "-o") {
8377cb4d3eSLandon J. Fuller			_i++
8477cb4d3eSLandon J. Fuller			if (_i >= ARGC)
85e83ce340SAdrian Chadd				usage()
86e83ce340SAdrian Chadd
8777cb4d3eSLandon J. Fuller			OUTPUT_FILE = ARGV[_i]
8877cb4d3eSLandon J. Fuller		} else if (ARGV[_i] == "--") {
8977cb4d3eSLandon J. Fuller			_i++
90e83ce340SAdrian Chadd			break
9177cb4d3eSLandon J. Fuller		} else if (ARGV[_i] !~ /^-/) {
9277cb4d3eSLandon J. Fuller			FILENAME = ARGV[_i]
93e83ce340SAdrian Chadd		} else {
9477cb4d3eSLandon J. Fuller			print "unknown option " ARGV[_i]
95e83ce340SAdrian Chadd			usage()
96e83ce340SAdrian Chadd		}
97e83ce340SAdrian Chadd	}
98e83ce340SAdrian Chadd
99e83ce340SAdrian Chadd	ARGC=2
100e83ce340SAdrian Chadd
101e83ce340SAdrian Chadd	if (OUT_T == null) {
102e83ce340SAdrian Chadd		print("error: one of -d or -h required")
103e83ce340SAdrian Chadd		usage()
104e83ce340SAdrian Chadd	}
105e83ce340SAdrian Chadd
106e83ce340SAdrian Chadd	if (FILENAME == null) {
107e83ce340SAdrian Chadd		print("error: no input file specified")
108e83ce340SAdrian Chadd		usage()
109e83ce340SAdrian Chadd	}
110e83ce340SAdrian Chadd
111e83ce340SAdrian Chadd	if (OUTPUT_FILE == "-") {
112e83ce340SAdrian Chadd		OUTPUT_FILE = "/dev/stdout"
113e83ce340SAdrian Chadd	} else if (OUTPUT_FILE == null) {
11477cb4d3eSLandon J. Fuller		OUTPUT_FILE_IDX = split(FILENAME, _g_output_path, "/")
11577cb4d3eSLandon J. Fuller		OUTPUT_FILE = _g_output_path[OUTPUT_FILE_IDX]
116e83ce340SAdrian Chadd
117e83ce340SAdrian Chadd		if (OUTPUT_FILE !~ /^bhnd_/)
118e83ce340SAdrian Chadd			OUTPUT_FILE = "bhnd_" OUTPUT_FILE
119e83ce340SAdrian Chadd
120e83ce340SAdrian Chadd		if (OUT_T == OUT_T_HEADER)
121e83ce340SAdrian Chadd			OUTPUT_FILE = OUTPUT_FILE ".h"
122e83ce340SAdrian Chadd		else
123e83ce340SAdrian Chadd			OUTPUT_FILE = OUTPUT_FILE "_data.h"
124e83ce340SAdrian Chadd	}
125e83ce340SAdrian Chadd
126e83ce340SAdrian Chadd	# Common Regexs
12777cb4d3eSLandon J. Fuller	UINT_REGEX	= "^(0|[1-9][0-9]*)$"
12877cb4d3eSLandon J. Fuller	HEX_REGEX	= "^(0x[A-Fa-f0-9]+)$"
12977cb4d3eSLandon J. Fuller	OFF_REGEX	= "^(0|[1-9][0-9]*)|^(0x[A-Fa-f0-9]+)"
13077cb4d3eSLandon J. Fuller	REL_OFF_REGEX	= "^\\+(0|[1-9][0-9]*)|^\\+(0x[A-Fa-f0-9]+)"
131e83ce340SAdrian Chadd
132e83ce340SAdrian Chadd	ARRAY_REGEX	= "\\[(0|[1-9][0-9]*)\\]"
13377cb4d3eSLandon J. Fuller	TYPES_REGEX	= "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?$"
134e83ce340SAdrian Chadd
13577cb4d3eSLandon J. Fuller	IDENT_REGEX		= "[A-Za-z_][A-Za-z0-9_]*"
13677cb4d3eSLandon J. Fuller	SVAR_IDENT_REGEX	= "^<"IDENT_REGEX">{?$"	# <var> identifiers
13777cb4d3eSLandon J. Fuller	VAR_IDENT_REGEX		= "^"IDENT_REGEX"{?$"	# var identifiers
138e83ce340SAdrian Chadd
13977cb4d3eSLandon J. Fuller	VACCESS_REGEX	= "^(private|internal)$"
140e83ce340SAdrian Chadd
14177cb4d3eSLandon J. Fuller	# Property array keys
14277cb4d3eSLandon J. Fuller	PROP_ID		= "p_id"
14377cb4d3eSLandon J. Fuller	PROP_NAME	= "p_name"
144e83ce340SAdrian Chadd
14577cb4d3eSLandon J. Fuller	# Prop path array keys
14677cb4d3eSLandon J. Fuller	PPATH_HEAD	= "ppath_head"
14777cb4d3eSLandon J. Fuller	PPATH_TAIL	= "ppath_tail"
148e83ce340SAdrian Chadd
14977cb4d3eSLandon J. Fuller	# Object array keys
15077cb4d3eSLandon J. Fuller	OBJ_IS_CLS	= "o_is_cls"
15177cb4d3eSLandon J. Fuller	OBJ_SUPER	= "o_super"
15277cb4d3eSLandon J. Fuller	OBJ_PROP	= "o_prop"
153e83ce340SAdrian Chadd
15477cb4d3eSLandon J. Fuller	# Class array keys
15577cb4d3eSLandon J. Fuller	CLS_NAME	= "cls_name"
15677cb4d3eSLandon J. Fuller	CLS_PROP	= "cls_prop"
157e83ce340SAdrian Chadd
15877cb4d3eSLandon J. Fuller	# C SPROM binding opcodes/opcode flags
15977cb4d3eSLandon J. Fuller	SPROM_OPCODE_EOF		= "SPROM_OPCODE_EOF"
16077cb4d3eSLandon J. Fuller	SPROM_OPCODE_NELEM		= "SPROM_OPCODE_NELEM"
16177cb4d3eSLandon J. Fuller	SPROM_OPCODE_VAR_END		= "SPROM_OPCODE_VAR_END"
16277cb4d3eSLandon J. Fuller	SPROM_OPCODE_VAR_IMM		= "SPROM_OPCODE_VAR_IMM"
16377cb4d3eSLandon J. Fuller	SPROM_OPCODE_VAR_REL_IMM	= "SPROM_OPCODE_VAR_REL_IMM"
16477cb4d3eSLandon J. Fuller	SPROM_OPCODE_VAR		= "SPROM_OPCODE_VAR"
16577cb4d3eSLandon J. Fuller	SPROM_OPCODE_REV_IMM		= "SPROM_OPCODE_REV_IMM"
16677cb4d3eSLandon J. Fuller	SPROM_OPCODE_REV_RANGE		= "SPROM_OPCODE_REV_RANGE"
16777cb4d3eSLandon J. Fuller	  SPROM_OP_REV_START_MASK	= "SPROM_OP_REV_START_MASK"
16877cb4d3eSLandon J. Fuller	  SPROM_OP_REV_START_SHIFT	= "SPROM_OP_REV_START_SHIFT"
16977cb4d3eSLandon J. Fuller	  SPROM_OP_REV_END_MASK		= "SPROM_OP_REV_END_MASK"
17077cb4d3eSLandon J. Fuller	  SPROM_OP_REV_END_SHIFT	= "SPROM_OP_REV_END_SHIFT"
17177cb4d3eSLandon J. Fuller	SPROM_OPCODE_MASK_IMM		= "SPROM_OPCODE_MASK_IMM"
17277cb4d3eSLandon J. Fuller	SPROM_OPCODE_MASK		= "SPROM_OPCODE_MASK"
17377cb4d3eSLandon J. Fuller	SPROM_OPCODE_SHIFT_IMM		= "SPROM_OPCODE_SHIFT_IMM"
17477cb4d3eSLandon J. Fuller	SPROM_OPCODE_SHIFT		= "SPROM_OPCODE_SHIFT"
17577cb4d3eSLandon J. Fuller	SPROM_OPCODE_OFFSET_REL_IMM	= "SPROM_OPCODE_OFFSET_REL_IMM"
17677cb4d3eSLandon J. Fuller	SPROM_OPCODE_OFFSET		= "SPROM_OPCODE_OFFSET"
17777cb4d3eSLandon J. Fuller	SPROM_OPCODE_TYPE		= "SPROM_OPCODE_TYPE"
17877cb4d3eSLandon J. Fuller	SPROM_OPCODE_TYPE_IMM		= "SPROM_OPCODE_TYPE_IMM"
17977cb4d3eSLandon J. Fuller	SPROM_OPCODE_DO_BINDN_IMM	= "SPROM_OPCODE_DO_BINDN_IMM"
18077cb4d3eSLandon J. Fuller	SPROM_OPCODE_DO_BIND		= "SPROM_OPCODE_DO_BIND"
18177cb4d3eSLandon J. Fuller	SPROM_OPCODE_DO_BINDN		= "SPROM_OPCODE_DO_BINDN"
18277cb4d3eSLandon J. Fuller	  SPROM_OP_BIND_SKIP_IN_MASK	= "SPROM_OP_BIND_SKIP_IN_MASK"
18377cb4d3eSLandon J. Fuller	  SPROM_OP_BIND_SKIP_IN_SHIFT	= "SPROM_OP_BIND_SKIP_IN_SHIFT"
18477cb4d3eSLandon J. Fuller	  SPROM_OP_BIND_SKIP_IN_SIGN	= "SPROM_OP_BIND_SKIP_IN_SIGN"
18577cb4d3eSLandon J. Fuller	  SPROM_OP_BIND_SKIP_OUT_MASK	= "SPROM_OP_BIND_SKIP_OUT_MASK"
18677cb4d3eSLandon J. Fuller	  SPROM_OP_BIND_SKIP_OUT_SHIFT	= "SPROM_OP_BIND_SKIP_OUT_SHIFT"
187e83ce340SAdrian Chadd
18877cb4d3eSLandon J. Fuller	SPROM_OP_DATA_U8		= "SPROM_OP_DATA_U8"
18977cb4d3eSLandon J. Fuller	SPROM_OP_DATA_U8_SCALED		= "SPROM_OP_DATA_U8_SCALED"
19077cb4d3eSLandon J. Fuller	SPROM_OP_DATA_U16		= "SPROM_OP_DATA_U16"
19177cb4d3eSLandon J. Fuller	SPROM_OP_DATA_U32		= "SPROM_OP_DATA_U32"
19277cb4d3eSLandon J. Fuller	SPROM_OP_DATA_I8		= "SPROM_OP_DATA_I8"
193e83ce340SAdrian Chadd
19477cb4d3eSLandon J. Fuller	SPROM_OP_BIND_SKIP_IN_MAX	=  3	# maximum SKIP_IN value
19577cb4d3eSLandon J. Fuller	SPROM_OP_BIND_SKIP_IN_MIN	= -3	# minimum SKIP_IN value
19677cb4d3eSLandon J. Fuller	SPROM_OP_BIND_SKIP_OUT_MAX	=  1	# maximum SKIP_OUT value
19777cb4d3eSLandon J. Fuller	SPROM_OP_BIND_SKIP_OUT_MIN	=  0	# minimum SKIP_OUT value
19877cb4d3eSLandon J. Fuller	SPROM_OP_IMM_MAX		= 15	# maximum immediate value
19977cb4d3eSLandon J. Fuller	SPROM_OP_REV_RANGE_MAX		= 15	# maximum SROM rev range value
20077cb4d3eSLandon J. Fuller
20177cb4d3eSLandon J. Fuller	# SPROM opcode encoding state
20277cb4d3eSLandon J. Fuller	SromOpStream = class_new("SromOpStream")
20377cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_layout, "layout")
20477cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_revisions, "revisions")
20577cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_vid, "vid")
20677cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_offset, "offset")
20777cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_type, "type")
20877cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_nelem, "nelem")
20977cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_mask, "mask")
21077cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_shift, "shift")
21177cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_bind_total, "bind_total")
21277cb4d3eSLandon J. Fuller		class_add_prop(SromOpStream, p_pending_bind, "pending_bind")
21377cb4d3eSLandon J. Fuller
21477cb4d3eSLandon J. Fuller	# SROM pending bind operation
21577cb4d3eSLandon J. Fuller	SromOpBind = class_new("SromOpBind")
21677cb4d3eSLandon J. Fuller		class_add_prop(SromOpBind, p_segment, "segment")
21777cb4d3eSLandon J. Fuller		class_add_prop(SromOpBind, p_count, "count")
21877cb4d3eSLandon J. Fuller		class_add_prop(SromOpBind, p_offset, "offset")
21977cb4d3eSLandon J. Fuller		class_add_prop(SromOpBind, p_width, "width")
22077cb4d3eSLandon J. Fuller		class_add_prop(SromOpBind, p_skip_in, "skip_in")
22177cb4d3eSLandon J. Fuller		class_add_prop(SromOpBind, p_skip_out, "skip_out")
22277cb4d3eSLandon J. Fuller		class_add_prop(SromOpBind, p_buffer, "buffer")
22377cb4d3eSLandon J. Fuller
22477cb4d3eSLandon J. Fuller	# Map class definition
22577cb4d3eSLandon J. Fuller	Map = class_new("Map")
22677cb4d3eSLandon J. Fuller
22777cb4d3eSLandon J. Fuller	# Array class definition
22877cb4d3eSLandon J. Fuller	Array = class_new("Array")
22977cb4d3eSLandon J. Fuller		class_add_prop(Array, p_count, "count")
23077cb4d3eSLandon J. Fuller
23177cb4d3eSLandon J. Fuller	# MacroType class definition
23277cb4d3eSLandon J. Fuller	# Used to define a set of known macro types that may be generated
23377cb4d3eSLandon J. Fuller	MacroType = class_new("MacroType")
23477cb4d3eSLandon J. Fuller		class_add_prop(MacroType, p_name, "name")
23577cb4d3eSLandon J. Fuller		class_add_prop(MacroType, p_const_suffix, "const_suffix")
23677cb4d3eSLandon J. Fuller
23777cb4d3eSLandon J. Fuller	MTypeVarName	= macro_type_new("name", "")		# var name
23877cb4d3eSLandon J. Fuller	MTypeVarID	= macro_type_new("id", "_ID")		# var unique ID
23977cb4d3eSLandon J. Fuller	MTypeVarMaxLen	= macro_type_new("len", "_MAXLEN")	# var max array length
24077cb4d3eSLandon J. Fuller
24177cb4d3eSLandon J. Fuller	# Preprocessor Constant
24277cb4d3eSLandon J. Fuller	MacroDefine = class_new("MacroDefine")
24377cb4d3eSLandon J. Fuller		class_add_prop(MacroDefine, p_name, "name")
24477cb4d3eSLandon J. Fuller		class_add_prop(MacroDefine, p_value, "value")
24577cb4d3eSLandon J. Fuller
24677cb4d3eSLandon J. Fuller	# ParseState definition
24777cb4d3eSLandon J. Fuller	ParseState = class_new("ParseState")
24877cb4d3eSLandon J. Fuller		class_add_prop(ParseState, p_ctx, "ctx")
24977cb4d3eSLandon J. Fuller		class_add_prop(ParseState, p_is_block, "is_block")
25077cb4d3eSLandon J. Fuller		class_add_prop(ParseState, p_line, "line")
25177cb4d3eSLandon J. Fuller
25277cb4d3eSLandon J. Fuller	# Value Formats
25377cb4d3eSLandon J. Fuller	Fmt = class_new("Fmt")
25477cb4d3eSLandon J. Fuller		class_add_prop(Fmt, p_name, "name")
255c283839dSLandon J. Fuller		class_add_prop(Fmt, p_symbol, "symbol")
256c283839dSLandon J. Fuller		class_add_prop(Fmt, p_array_fmt, "array_fmt")
25777cb4d3eSLandon J. Fuller
25877cb4d3eSLandon J. Fuller	FmtHex		= fmt_new("hex", "bhnd_nvram_val_bcm_hex_fmt")
25977cb4d3eSLandon J. Fuller	FmtDec 		= fmt_new("decimal", "bhnd_nvram_val_bcm_decimal_fmt")
26077cb4d3eSLandon J. Fuller	FmtMAC		= fmt_new("macaddr", "bhnd_nvram_val_bcm_macaddr_fmt")
26177cb4d3eSLandon J. Fuller	FmtLEDDC	= fmt_new("leddc", "bhnd_nvram_val_bcm_leddc_fmt")
262c283839dSLandon J. Fuller	FmtCharArray	= fmt_new("char_array", "bhnd_nvram_val_char_array_fmt")
263c283839dSLandon J. Fuller	FmtChar		= fmt_new("char", "bhnd_nvram_val_char_array_fmt",
264c283839dSLandon J. Fuller			      FmtCharArray)
26577cb4d3eSLandon J. Fuller	FmtStr		= fmt_new("string", "bhnd_nvram_val_bcm_string_fmt")
26677cb4d3eSLandon J. Fuller
267c283839dSLandon J. Fuller	# User-specifiable value formats
26877cb4d3eSLandon J. Fuller	ValueFormats = map_new()
26977cb4d3eSLandon J. Fuller		map_set(ValueFormats, get(FmtHex,	p_name), FmtHex)
27077cb4d3eSLandon J. Fuller		map_set(ValueFormats, get(FmtDec,	p_name), FmtDec)
27177cb4d3eSLandon J. Fuller		map_set(ValueFormats, get(FmtMAC,	p_name), FmtMAC)
27277cb4d3eSLandon J. Fuller		map_set(ValueFormats, get(FmtLEDDC,	p_name), FmtLEDDC)
27377cb4d3eSLandon J. Fuller		map_set(ValueFormats, get(FmtStr,	p_name), FmtStr)
27477cb4d3eSLandon J. Fuller
27577cb4d3eSLandon J. Fuller	# Data Types
27677cb4d3eSLandon J. Fuller	Type = class_new("Type")
27777cb4d3eSLandon J. Fuller		class_add_prop(Type, p_name, "name")
27877cb4d3eSLandon J. Fuller		class_add_prop(Type, p_width, "width")
27977cb4d3eSLandon J. Fuller		class_add_prop(Type, p_signed, "signed")
28077cb4d3eSLandon J. Fuller		class_add_prop(Type, p_const, "const")
28177cb4d3eSLandon J. Fuller		class_add_prop(Type, p_const_val, "const_val")
28277cb4d3eSLandon J. Fuller		class_add_prop(Type, p_array_const, "array_const")
28377cb4d3eSLandon J. Fuller		class_add_prop(Type, p_array_const_val, "array_const_val")
28477cb4d3eSLandon J. Fuller		class_add_prop(Type, p_default_fmt, "default_fmt")
28577cb4d3eSLandon J. Fuller		class_add_prop(Type, p_mask, "mask")
28677cb4d3eSLandon J. Fuller
28777cb4d3eSLandon J. Fuller	ArrayType = class_new("ArrayType", AST)
28877cb4d3eSLandon J. Fuller		class_add_prop(ArrayType, p_type, "type")
28977cb4d3eSLandon J. Fuller		class_add_prop(ArrayType, p_count, "count")
29077cb4d3eSLandon J. Fuller
29177cb4d3eSLandon J. Fuller	UInt8Max	=  255
29277cb4d3eSLandon J. Fuller	UInt16Max	=  65535
29377cb4d3eSLandon J. Fuller	UInt32Max	=  4294967295
29477cb4d3eSLandon J. Fuller	Int8Min		= -128
29577cb4d3eSLandon J. Fuller	Int8Max		=  127
29677cb4d3eSLandon J. Fuller	Int16Min	= -32768
29777cb4d3eSLandon J. Fuller	Int16Max	=  32767
29877cb4d3eSLandon J. Fuller	Int32Min	= -2147483648
29977cb4d3eSLandon J. Fuller	Int32Max	=  2147483648
30077cb4d3eSLandon J. Fuller	CharMin		=  Int8Min
30177cb4d3eSLandon J. Fuller	CharMax		=  Int8Max
30277cb4d3eSLandon J. Fuller
30377cb4d3eSLandon J. Fuller	UInt8	= type_new("u8", 1, 0, "BHND_NVRAM_TYPE_UINT8",
30477cb4d3eSLandon J. Fuller	   "BHND_NVRAM_TYPE_UINT8_ARRAY", FmtHex, UInt8Max, 0, 16)
30577cb4d3eSLandon J. Fuller
30677cb4d3eSLandon J. Fuller	UInt16	= type_new("u16", 2, 0, "BHND_NVRAM_TYPE_UINT16",
30777cb4d3eSLandon J. Fuller	   "BHND_NVRAM_TYPE_UINT16_ARRAY", FmtHex, UInt16Max, 1, 17)
30877cb4d3eSLandon J. Fuller
30977cb4d3eSLandon J. Fuller	UInt32	= type_new("u32", 4, 0, "BHND_NVRAM_TYPE_UINT32",
31077cb4d3eSLandon J. Fuller	   "BHND_NVRAM_TYPE_UINT32_ARRAY", FmtHex, UInt32Max, 2, 18)
31177cb4d3eSLandon J. Fuller
31277cb4d3eSLandon J. Fuller	Int8	= type_new("i8", 1, 1, "BHND_NVRAM_TYPE_INT8",
31377cb4d3eSLandon J. Fuller	   "BHND_NVRAM_TYPE_INT8_ARRAY", FmtDec, UInt8Max, 4, 20)
31477cb4d3eSLandon J. Fuller
31577cb4d3eSLandon J. Fuller	Int16	= type_new("i16", 2, 1, "BHND_NVRAM_TYPE_INT16",
31677cb4d3eSLandon J. Fuller	   "BHND_NVRAM_TYPE_INT16_ARRAY", FmtDec, UInt16Max, 5, 21)
31777cb4d3eSLandon J. Fuller
31877cb4d3eSLandon J. Fuller	Int32	= type_new("i32", 4, 1, "BHND_NVRAM_TYPE_INT32",
31977cb4d3eSLandon J. Fuller	   "BHND_NVRAM_TYPE_INT32_ARRAY", FmtDec, UInt32Max, 6, 22)
32077cb4d3eSLandon J. Fuller
32177cb4d3eSLandon J. Fuller	Char	= type_new("char", 1, 1, "BHND_NVRAM_TYPE_CHAR",
322c283839dSLandon J. Fuller	   "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtChar, UInt8Max, 8, 24)
32377cb4d3eSLandon J. Fuller
32477cb4d3eSLandon J. Fuller	BaseTypes = map_new()
32577cb4d3eSLandon J. Fuller		map_set(BaseTypes, get(UInt8,	p_name), UInt8)
32677cb4d3eSLandon J. Fuller		map_set(BaseTypes, get(UInt16,	p_name), UInt16)
32777cb4d3eSLandon J. Fuller		map_set(BaseTypes, get(UInt32,	p_name), UInt32)
32877cb4d3eSLandon J. Fuller		map_set(BaseTypes, get(Int8,	p_name), Int8)
32977cb4d3eSLandon J. Fuller		map_set(BaseTypes, get(Int16,	p_name), Int16)
33077cb4d3eSLandon J. Fuller		map_set(BaseTypes, get(Int32,	p_name), Int32)
33177cb4d3eSLandon J. Fuller		map_set(BaseTypes, get(Char,	p_name), Char)
33277cb4d3eSLandon J. Fuller
33377cb4d3eSLandon J. Fuller	BaseTypesArray = map_to_array(BaseTypes)
33477cb4d3eSLandon J. Fuller	BaseTypesCount = array_size(BaseTypesArray)
33577cb4d3eSLandon J. Fuller
33677cb4d3eSLandon J. Fuller	# Variable Flags
33777cb4d3eSLandon J. Fuller	VFlag = class_new("VFlag")
33877cb4d3eSLandon J. Fuller		class_add_prop(VFlag, p_name, "name")
33977cb4d3eSLandon J. Fuller		class_add_prop(VFlag, p_const, "const")
34077cb4d3eSLandon J. Fuller
34177cb4d3eSLandon J. Fuller	VFlagPrivate	= vflag_new("private", "BHND_NVRAM_VF_MFGINT")
34277cb4d3eSLandon J. Fuller	VFlagIgnoreAll1	= vflag_new("ignall1", "BHND_NVRAM_VF_IGNALL1")
34377cb4d3eSLandon J. Fuller
34477cb4d3eSLandon J. Fuller	# Variable Access Type Constants
34577cb4d3eSLandon J. Fuller	VAccess = class_new("VAccess")
34677cb4d3eSLandon J. Fuller	VAccessPublic	= obj_new(VAccess)	# Public
34777cb4d3eSLandon J. Fuller	VAccessPrivate	= obj_new(VAccess)	# MFG Private
34877cb4d3eSLandon J. Fuller	VAccessInternal	= obj_new(VAccess)	# Implementation-Internal
34977cb4d3eSLandon J. Fuller
35077cb4d3eSLandon J. Fuller	#
35177cb4d3eSLandon J. Fuller	# AST node classes
35277cb4d3eSLandon J. Fuller	#
35377cb4d3eSLandon J. Fuller	AST = class_new("AST")
35477cb4d3eSLandon J. Fuller		class_add_prop(AST, p_line, "line")
35577cb4d3eSLandon J. Fuller
35677cb4d3eSLandon J. Fuller	SymbolContext = class_new("SymbolContext", AST)
35777cb4d3eSLandon J. Fuller		class_add_prop(SymbolContext, p_vars, "vars")
35877cb4d3eSLandon J. Fuller
35977cb4d3eSLandon J. Fuller	# NVRAM root parser context
36077cb4d3eSLandon J. Fuller	NVRAM = class_new("NVRAM", SymbolContext)
36177cb4d3eSLandon J. Fuller		class_add_prop(NVRAM, p_var_groups, "var_groups")
36277cb4d3eSLandon J. Fuller		class_add_prop(NVRAM, p_srom_layouts, "srom_layouts")
36377cb4d3eSLandon J. Fuller		class_add_prop(NVRAM, p_srom_table, "srom_table")
36477cb4d3eSLandon J. Fuller
36577cb4d3eSLandon J. Fuller	# Variable Group
36677cb4d3eSLandon J. Fuller	VarGroup = class_new("VarGroup", SymbolContext)
36777cb4d3eSLandon J. Fuller		class_add_prop(VarGroup, p_name, "name")
36877cb4d3eSLandon J. Fuller
36977cb4d3eSLandon J. Fuller	# Revision Range
37077cb4d3eSLandon J. Fuller	RevRange = class_new("RevRange", AST)
37177cb4d3eSLandon J. Fuller		class_add_prop(RevRange, p_start, "start")
37277cb4d3eSLandon J. Fuller		class_add_prop(RevRange, p_end, "end")
37377cb4d3eSLandon J. Fuller
37477cb4d3eSLandon J. Fuller	# String Constant
37577cb4d3eSLandon J. Fuller	StringConstant = class_new("StringConstant", AST)
37677cb4d3eSLandon J. Fuller		class_add_prop(StringConstant, p_value, "value")		# string
37777cb4d3eSLandon J. Fuller		class_add_prop(StringConstant, p_continued, "continued")	# bool
37877cb4d3eSLandon J. Fuller
37977cb4d3eSLandon J. Fuller	# Variable Declaration
38077cb4d3eSLandon J. Fuller	Var = class_new("Var", AST)
38177cb4d3eSLandon J. Fuller		class_add_prop(Var, p_access, "access")		# VAccess
38277cb4d3eSLandon J. Fuller		class_add_prop(Var, p_name, "name")		# string
38377cb4d3eSLandon J. Fuller		class_add_prop(Var, p_desc, "desc")		# StringConstant
38477cb4d3eSLandon J. Fuller		class_add_prop(Var, p_help, "help")		# StringConstant
38577cb4d3eSLandon J. Fuller		class_add_prop(Var, p_type, "type")		# AbstractType
38677cb4d3eSLandon J. Fuller		class_add_prop(Var, p_fmt, "fmt")		# Fmt
38777cb4d3eSLandon J. Fuller		class_add_prop(Var, p_ignall1, "ignall1")	# bool
38877cb4d3eSLandon J. Fuller		# ID is assigned once all variables are sorted
38977cb4d3eSLandon J. Fuller		class_add_prop(Var, p_vid, "vid")		# int
39077cb4d3eSLandon J. Fuller
39177cb4d3eSLandon J. Fuller	# Common interface inherited by parser contexts that support
39277cb4d3eSLandon J. Fuller	# registration of SROM variable entries
39377cb4d3eSLandon J. Fuller	SromContext = class_new("SromContext", AST)
39477cb4d3eSLandon J. Fuller		class_add_prop(SromContext, p_revisions, "revisions")
39577cb4d3eSLandon J. Fuller
39677cb4d3eSLandon J. Fuller	# SROM Layout Node
39777cb4d3eSLandon J. Fuller	SromLayout = class_new("SromLayout", SromContext)
39877cb4d3eSLandon J. Fuller		class_add_prop(SromLayout, p_entries, "entries")	# Array<SromEntry>
39977cb4d3eSLandon J. Fuller		class_add_prop(SromLayout, p_revmap, "revmap")		# Map<(string,int), SromEntry>
40077cb4d3eSLandon J. Fuller		class_add_prop(SromLayout, p_output_var_counts,		# Map<int, int> (rev->count)
40177cb4d3eSLandon J. Fuller		    "output_var_counts")
40277cb4d3eSLandon J. Fuller
40377cb4d3eSLandon J. Fuller	# SROM Layout Filter Node
40477cb4d3eSLandon J. Fuller	# Represents a filter over a parent SromLayout's revisions
40577cb4d3eSLandon J. Fuller	SromLayoutFilter = class_new("SromLayoutFilter", SromContext)
40677cb4d3eSLandon J. Fuller		class_add_prop(SromLayoutFilter, p_parent, "parent")
40777cb4d3eSLandon J. Fuller
40877cb4d3eSLandon J. Fuller	# SROM variable entry
40977cb4d3eSLandon J. Fuller	SromEntry = class_new("SromEntry", AST)
41077cb4d3eSLandon J. Fuller		class_add_prop(SromEntry, p_var, "var")
41177cb4d3eSLandon J. Fuller		class_add_prop(SromEntry, p_revisions, "revisions")
41277cb4d3eSLandon J. Fuller		class_add_prop(SromEntry, p_base_offset, "base_offset")
41377cb4d3eSLandon J. Fuller		class_add_prop(SromEntry, p_type, "type")
41477cb4d3eSLandon J. Fuller		class_add_prop(SromEntry, p_offsets, "offsets")
41577cb4d3eSLandon J. Fuller
41677cb4d3eSLandon J. Fuller	# SROM variable offset
41777cb4d3eSLandon J. Fuller	SromOffset = class_new("SromOffset", AST)
41877cb4d3eSLandon J. Fuller		class_add_prop(SromOffset, p_segments, "segments")
41977cb4d3eSLandon J. Fuller
42077cb4d3eSLandon J. Fuller	# SROM variable offset segment
42177cb4d3eSLandon J. Fuller	SromSegment = class_new("SromSegment", AST)
42277cb4d3eSLandon J. Fuller		class_add_prop(SromSegment, p_offset, "offset")
42377cb4d3eSLandon J. Fuller		class_add_prop(SromSegment, p_type, "type")
42477cb4d3eSLandon J. Fuller		class_add_prop(SromSegment, p_mask, "mask")
42577cb4d3eSLandon J. Fuller		class_add_prop(SromSegment, p_shift, "shift")
42677cb4d3eSLandon J. Fuller		class_add_prop(SromSegment, p_value, "value")
42777cb4d3eSLandon J. Fuller
42877cb4d3eSLandon J. Fuller	# Create the parse state stack
42977cb4d3eSLandon J. Fuller	_g_parse_stack_depth = 0
43077cb4d3eSLandon J. Fuller	_g_parse_stack[0] = null
43177cb4d3eSLandon J. Fuller
43277cb4d3eSLandon J. Fuller	# Push the root parse state
43377cb4d3eSLandon J. Fuller	parser_state_push(nvram_new(), 0)
434e83ce340SAdrian Chadd}
435e83ce340SAdrian Chadd
43677cb4d3eSLandon J. Fullerfunction at_exit(_block_start, _state, _output_vars, _noutput_vars, _name, _var,
43777cb4d3eSLandon J. Fuller    _i)
438e83ce340SAdrian Chadd{
439e83ce340SAdrian Chadd	# Skip completion handling if exiting from an error
440e83ce340SAdrian Chadd	if (_EARLY_EXIT)
441e83ce340SAdrian Chadd		exit 1
442e83ce340SAdrian Chadd
443e83ce340SAdrian Chadd	# Check for complete block closure
44477cb4d3eSLandon J. Fuller	if (!in_parser_context(NVRAM)) {
44577cb4d3eSLandon J. Fuller		_state = parser_state_get()
44677cb4d3eSLandon J. Fuller		_block_start = get(_state, p_line)
44777cb4d3eSLandon J. Fuller		errorx("missing '}' for block opened on line " _block_start "")
448e83ce340SAdrian Chadd	}
449e83ce340SAdrian Chadd
45077cb4d3eSLandon J. Fuller	# Apply lexicographical sorting to our variable names. To support more
45177cb4d3eSLandon J. Fuller	# effecient table searching, we guarantee a stable sort order (using C
45277cb4d3eSLandon J. Fuller	# collation).
45377cb4d3eSLandon J. Fuller	#
45477cb4d3eSLandon J. Fuller	# This also has a side-effect of generating a unique monotonic ID
45577cb4d3eSLandon J. Fuller	# for all variables, which we will emit as a #define and can use as a
45677cb4d3eSLandon J. Fuller	# direct index into the C variable table
45777cb4d3eSLandon J. Fuller	_output_vars = array_new()
45877cb4d3eSLandon J. Fuller	for (_name in _g_var_names) {
45977cb4d3eSLandon J. Fuller		_var = _g_var_names[_name]
46077cb4d3eSLandon J. Fuller
46177cb4d3eSLandon J. Fuller		# Don't include internal variables in the output
46277cb4d3eSLandon J. Fuller		if (var_is_internal(_var))
46377cb4d3eSLandon J. Fuller			continue
46477cb4d3eSLandon J. Fuller
46577cb4d3eSLandon J. Fuller		array_append(_output_vars, _var)
466e83ce340SAdrian Chadd	}
467e83ce340SAdrian Chadd
46877cb4d3eSLandon J. Fuller	# Sort by variable name
46977cb4d3eSLandon J. Fuller	array_sort(_output_vars, prop_to_path(p_name))
47077cb4d3eSLandon J. Fuller
47177cb4d3eSLandon J. Fuller	# Set all variable ID properties to their newly assigned ID value
47277cb4d3eSLandon J. Fuller	_noutput_vars = array_size(_output_vars)
47377cb4d3eSLandon J. Fuller	for (_i = 0; _i < _noutput_vars; _i++) {
47477cb4d3eSLandon J. Fuller		_var = array_get(_output_vars, _i)
47577cb4d3eSLandon J. Fuller		set(_var, p_vid, _i)
47677cb4d3eSLandon J. Fuller	}
477e83ce340SAdrian Chadd
478e83ce340SAdrian Chadd	# Truncate output file and write common header
479e83ce340SAdrian Chadd	printf("") > OUTPUT_FILE
480e83ce340SAdrian Chadd	emit("/*\n")
481e83ce340SAdrian Chadd	emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n")
482e83ce340SAdrian Chadd	emit(" *\n")
483e83ce340SAdrian Chadd	emit(" * generated from nvram map: " FILENAME "\n")
484e83ce340SAdrian Chadd	emit(" */\n")
485e83ce340SAdrian Chadd	emit("\n")
486e83ce340SAdrian Chadd
487e83ce340SAdrian Chadd	# Emit all variable definitions
488e83ce340SAdrian Chadd	if (OUT_T == OUT_T_DATA) {
48977cb4d3eSLandon J. Fuller		write_data(_output_vars)
490e83ce340SAdrian Chadd	} else if (OUT_T == OUT_T_HEADER) {
49177cb4d3eSLandon J. Fuller		write_header(_output_vars)
492e83ce340SAdrian Chadd	}
493*4f4eb606SMatt Macy	if (VERBOSE == 1) {
49477cb4d3eSLandon J. Fuller	        printf("%u variable records written to %s\n", array_size(_output_vars),
495e83ce340SAdrian Chadd		       OUTPUT_FILE) >> "/dev/stderr"
496e83ce340SAdrian Chadd	}
497*4f4eb606SMatt Macy}
498e83ce340SAdrian Chadd
49977cb4d3eSLandon J. Fuller# Write the public header (output type HEADER)
50077cb4d3eSLandon J. Fullerfunction write_header(output_vars, _noutput_vars, _var,
50177cb4d3eSLandon J. Fuller    _tab_align, _macro, _macros, _num_macros, _i)
502e83ce340SAdrian Chadd{
50377cb4d3eSLandon J. Fuller	# Produce our array of #defines
50477cb4d3eSLandon J. Fuller	_num_macros = 0
50577cb4d3eSLandon J. Fuller	_noutput_vars = array_size(output_vars)
50677cb4d3eSLandon J. Fuller	for (_i = 0; _i < _noutput_vars; _i++) {
50777cb4d3eSLandon J. Fuller		_var = array_get(output_vars, _i)
50877cb4d3eSLandon J. Fuller
50977cb4d3eSLandon J. Fuller		# Variable name
51077cb4d3eSLandon J. Fuller		_macro = var_get_macro(_var, MTypeVarName, \
51177cb4d3eSLandon J. Fuller		    "\"" get(_var, p_name) "\"")
51277cb4d3eSLandon J. Fuller		_macros[_num_macros++] = _macro
51377cb4d3eSLandon J. Fuller
51477cb4d3eSLandon J. Fuller		# Variable array length
51577cb4d3eSLandon J. Fuller		if (var_has_array_type(_var)) {
51677cb4d3eSLandon J. Fuller			_macro = var_get_macro(_var, MTypeVarMaxLen,
51777cb4d3eSLandon J. Fuller			    var_get_array_len(_var))
51877cb4d3eSLandon J. Fuller			_macros[_num_macros++] = _macro
51977cb4d3eSLandon J. Fuller		}
520e83ce340SAdrian Chadd	}
521e83ce340SAdrian Chadd
52277cb4d3eSLandon J. Fuller	# Calculate value tab alignment position for our macros
52377cb4d3eSLandon J. Fuller	_tab_align = macros_get_tab_alignment(_macros, _num_macros)
524e83ce340SAdrian Chadd
52577cb4d3eSLandon J. Fuller	# Write the macros
52677cb4d3eSLandon J. Fuller	for (_i = 0; _i < _num_macros; _i++)
52777cb4d3eSLandon J. Fuller		write_macro_define(_macros[_i], _tab_align)
52877cb4d3eSLandon J. Fuller}
52977cb4d3eSLandon J. Fuller
53077cb4d3eSLandon J. Fuller# Write the private data header (output type DATA)
53177cb4d3eSLandon J. Fullerfunction write_data(output_vars, _noutput_vars, _var, _nvram, _layouts,
53277cb4d3eSLandon J. Fuller    _nlayouts, _layout, _revs, _rev, _rev_start, _rev_end, _base_type,
53377cb4d3eSLandon J. Fuller    _srom_table, _nsrom_table, _i, _j)
53477cb4d3eSLandon J. Fuller{
53577cb4d3eSLandon J. Fuller	_nvram = parser_state_get_context(NVRAM)
53677cb4d3eSLandon J. Fuller	_layouts = get(_nvram, p_srom_layouts)
53777cb4d3eSLandon J. Fuller	_nlayouts = array_size(_layouts)
53877cb4d3eSLandon J. Fuller
53977cb4d3eSLandon J. Fuller	_noutput_vars = array_size(output_vars)
54077cb4d3eSLandon J. Fuller
54177cb4d3eSLandon J. Fuller	# Write all our private NVAR_ID defines
54277cb4d3eSLandon J. Fuller	write_data_defines(output_vars)
54377cb4d3eSLandon J. Fuller
54477cb4d3eSLandon J. Fuller	# Write all layout binding opcodes, and build an array
54577cb4d3eSLandon J. Fuller	# mapping SROM revision to corresponding SROM layout
54677cb4d3eSLandon J. Fuller	_srom_table = array_new()
54777cb4d3eSLandon J. Fuller	for (_i = 0; _i < _nlayouts; _i++) {
54877cb4d3eSLandon J. Fuller		_layout = array_get(_layouts, _i)
54977cb4d3eSLandon J. Fuller
55077cb4d3eSLandon J. Fuller		# Write binding opcode table to our output file
55177cb4d3eSLandon J. Fuller		write_srom_bindings(_layout)
55277cb4d3eSLandon J. Fuller
55377cb4d3eSLandon J. Fuller		# Add entries to _srom_table for all covered revisions
55477cb4d3eSLandon J. Fuller		_revs = get(_layout, p_revisions)
55577cb4d3eSLandon J. Fuller		_rev_start = get(_revs, p_start)
55677cb4d3eSLandon J. Fuller		_rev_end = get(_revs, p_end)
55777cb4d3eSLandon J. Fuller
55877cb4d3eSLandon J. Fuller		for (_j = _rev_start; _j <= _rev_end; _j++)
55977cb4d3eSLandon J. Fuller			array_append(_srom_table, _j)
56077cb4d3eSLandon J. Fuller	}
56177cb4d3eSLandon J. Fuller
56277cb4d3eSLandon J. Fuller	# Sort in ascending order, by SROM revision
56377cb4d3eSLandon J. Fuller	array_sort(_srom_table)
56477cb4d3eSLandon J. Fuller	_nsrom_table = array_size(_srom_table)
56577cb4d3eSLandon J. Fuller
56677cb4d3eSLandon J. Fuller	# Write the variable definitions
56777cb4d3eSLandon J. Fuller	emit("/* Variable definitions */\n")
56877cb4d3eSLandon J. Fuller	emit("const struct bhnd_nvram_vardefn " \
56977cb4d3eSLandon J. Fuller	    "bhnd_nvram_vardefns[] = {\n")
57077cb4d3eSLandon J. Fuller	output_depth++
57177cb4d3eSLandon J. Fuller	for (_i = 0; _i < _noutput_vars; _i++) {
57277cb4d3eSLandon J. Fuller		write_data_nvram_vardefn(array_get(output_vars, _i))
57377cb4d3eSLandon J. Fuller	}
57477cb4d3eSLandon J. Fuller	output_depth--
57577cb4d3eSLandon J. Fuller	emit("};\n")
57677cb4d3eSLandon J. Fuller	emit("const size_t bhnd_nvram_num_vardefns = " _noutput_vars ";\n")
57777cb4d3eSLandon J. Fuller
57877cb4d3eSLandon J. Fuller	# Write static asserts for raw type constant values that must be kept
57977cb4d3eSLandon J. Fuller	# synchronized with the code
58077cb4d3eSLandon J. Fuller	for (_i = 0; _i < BaseTypesCount; _i++) {
58177cb4d3eSLandon J. Fuller		_base_type = array_get(BaseTypesArray, _i)
58277cb4d3eSLandon J. Fuller
58377cb4d3eSLandon J. Fuller		emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
58477cb4d3eSLandon J. Fuller		    type_get_const(_base_type), type_get_const_val(_base_type),
58577cb4d3eSLandon J. Fuller		    "type constant out of sync"))
58677cb4d3eSLandon J. Fuller
58777cb4d3eSLandon J. Fuller		emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
58877cb4d3eSLandon J. Fuller		    get(_base_type, p_array_const),
58977cb4d3eSLandon J. Fuller		    get(_base_type, p_array_const_val),
59077cb4d3eSLandon J. Fuller		    "array type constant out of sync"))
59177cb4d3eSLandon J. Fuller	}
59277cb4d3eSLandon J. Fuller
59377cb4d3eSLandon J. Fuller	# Write all top-level bhnd_sprom_layout entries
59477cb4d3eSLandon J. Fuller	emit("/* SPROM layouts */\n")
59577cb4d3eSLandon J. Fuller	emit("const struct bhnd_sprom_layout bhnd_sprom_layouts[] = {\n")
59677cb4d3eSLandon J. Fuller	output_depth++
59777cb4d3eSLandon J. Fuller	for (_i = 0; _i < _nsrom_table; _i++) {
59877cb4d3eSLandon J. Fuller		_rev = array_get(_srom_table, _i)
59977cb4d3eSLandon J. Fuller		_layout = nvram_get_srom_layout(_nvram, _rev)
60077cb4d3eSLandon J. Fuller		write_data_srom_layout(_layout, _rev)
60177cb4d3eSLandon J. Fuller	}
60277cb4d3eSLandon J. Fuller	output_depth--
60377cb4d3eSLandon J. Fuller	emit("};\n")
60477cb4d3eSLandon J. Fuller	emit("const size_t bhnd_sprom_num_layouts = " _nsrom_table ";\n")
60577cb4d3eSLandon J. Fuller}
60677cb4d3eSLandon J. Fuller
60777cb4d3eSLandon J. Fuller# Write a bhnd_nvram_vardef entry for the given variable
60877cb4d3eSLandon J. Fullerfunction write_data_nvram_vardefn(v, _desc, _help, _type, _fmt) {
60977cb4d3eSLandon J. Fuller	obj_assert_class(v, Var)
61077cb4d3eSLandon J. Fuller
61177cb4d3eSLandon J. Fuller	_desc = get(v, p_desc)
61277cb4d3eSLandon J. Fuller	_help = get(v, p_help)
61377cb4d3eSLandon J. Fuller	_type = get(v, p_type)
61477cb4d3eSLandon J. Fuller	_fmt = var_get_fmt(v)
61577cb4d3eSLandon J. Fuller
61677cb4d3eSLandon J. Fuller	emit("{\n")
61777cb4d3eSLandon J. Fuller	output_depth++
61877cb4d3eSLandon J. Fuller	emit(sprintf(".name = \"%s\",\n", get(v, p_name)))
61977cb4d3eSLandon J. Fuller
62077cb4d3eSLandon J. Fuller	if (_desc != null)
62177cb4d3eSLandon J. Fuller		emit(sprintf(".desc = \"%s\",\n", get(_desc, p_value)))
62277cb4d3eSLandon J. Fuller	else
62377cb4d3eSLandon J. Fuller		emit(".desc = NULL,\n")
62477cb4d3eSLandon J. Fuller
62577cb4d3eSLandon J. Fuller	if (_help != null)
62677cb4d3eSLandon J. Fuller		emit(sprintf(".help = \"%s\",\n", get(_help, p_value)))
62777cb4d3eSLandon J. Fuller	else
62877cb4d3eSLandon J. Fuller		emit(".help = NULL,\n")
62977cb4d3eSLandon J. Fuller
63077cb4d3eSLandon J. Fuller	emit(".type = " type_get_const(_type) ",\n")
63177cb4d3eSLandon J. Fuller	emit(".nelem = " var_get_array_len(v) ",\n")
63277cb4d3eSLandon J. Fuller	emit(".fmt = &" get(_fmt, p_symbol) ",\n")
63377cb4d3eSLandon J. Fuller	emit(".flags = " gen_var_flags(v) ",\n")
63477cb4d3eSLandon J. Fuller
63577cb4d3eSLandon J. Fuller	output_depth--
63677cb4d3eSLandon J. Fuller	emit("},\n")
63777cb4d3eSLandon J. Fuller}
63877cb4d3eSLandon J. Fuller
63977cb4d3eSLandon J. Fuller# Write a top-level bhnd_sprom_layout entry for the given revision
64077cb4d3eSLandon J. Fuller# and layout definition
64177cb4d3eSLandon J. Fullerfunction write_data_srom_layout(layout, revision, _flags, _size,
642c283839dSLandon J. Fuller    _sromcrc, _crc_seg, _crc_off,
64377cb4d3eSLandon J. Fuller    _sromsig, _sig_seg, _sig_offset, _sig_value,
64477cb4d3eSLandon J. Fuller    _sromrev, _rev_seg, _rev_off,
64577cb4d3eSLandon J. Fuller    _num_vars)
64677cb4d3eSLandon J. Fuller{
64777cb4d3eSLandon J. Fuller	_flags = array_new()
64877cb4d3eSLandon J. Fuller
64977cb4d3eSLandon J. Fuller	# Calculate the size; it always follows the internal CRC variable
65077cb4d3eSLandon J. Fuller	_sromcrc = srom_layout_find_entry(layout, "<sromcrc>", revision)
65177cb4d3eSLandon J. Fuller	if (_sromcrc == null) {
65277cb4d3eSLandon J. Fuller		errorx("missing '<sromcrc>' entry for '"revision"' layout, " \
65377cb4d3eSLandon J. Fuller		    "cannot compute total size")
65477cb4d3eSLandon J. Fuller	} else {
65577cb4d3eSLandon J. Fuller		_crc_seg = srom_entry_get_single_segment(_sromcrc)
656c283839dSLandon J. Fuller		_crc_off = get(_crc_seg, p_offset)
657c283839dSLandon J. Fuller		_size = _crc_off
65877cb4d3eSLandon J. Fuller		_size += get(get(_crc_seg, p_type), p_width)
65977cb4d3eSLandon J. Fuller	}
66077cb4d3eSLandon J. Fuller
66177cb4d3eSLandon J. Fuller	# Fetch signature definition
66277cb4d3eSLandon J. Fuller	_sromsig = srom_layout_find_entry(layout, "<sromsig>", revision)
66377cb4d3eSLandon J. Fuller	if (_sromsig == null) {
66477cb4d3eSLandon J. Fuller		array_append(_flags, "SPROM_LAYOUT_MAGIC_NONE")
66577cb4d3eSLandon J. Fuller	} else {
66677cb4d3eSLandon J. Fuller		_sig_seg = srom_entry_get_single_segment(_sromsig)
66777cb4d3eSLandon J. Fuller
66877cb4d3eSLandon J. Fuller		_sig_offset = get(_sig_seg, p_offset)
66977cb4d3eSLandon J. Fuller		_sig_value = get(_sig_seg, p_value)
67077cb4d3eSLandon J. Fuller		if (_sig_value == "")
67177cb4d3eSLandon J. Fuller			errorc(get(_sromsig, p_line), "missing signature value")
67277cb4d3eSLandon J. Fuller	}
67377cb4d3eSLandon J. Fuller
67477cb4d3eSLandon J. Fuller	# Fetch sromrev definition
67577cb4d3eSLandon J. Fuller	_sromrev = srom_layout_find_entry(layout, "sromrev", revision)
67677cb4d3eSLandon J. Fuller	if (_sromrev == null) {
67777cb4d3eSLandon J. Fuller		errorx("missing 'sromrev' entry for '"revision"' layout, " \
67877cb4d3eSLandon J. Fuller		    "cannot determine offset")
67977cb4d3eSLandon J. Fuller	} else {
68077cb4d3eSLandon J. Fuller		# Must be a u8 value
68177cb4d3eSLandon J. Fuller		if (!type_equal(get(_sromrev, p_type), UInt8)) {
68277cb4d3eSLandon J. Fuller			errorx("'sromrev' entry has non-u8 type '" \
68377cb4d3eSLandon J. Fuller			    type_to_string(get(_sromrev, p_type)))
68477cb4d3eSLandon J. Fuller		}
68577cb4d3eSLandon J. Fuller
68677cb4d3eSLandon J. Fuller		_rev_seg = srom_entry_get_single_segment(_sromrev)
68777cb4d3eSLandon J. Fuller		_rev_off = get(_rev_seg, p_offset)
68877cb4d3eSLandon J. Fuller	}
68977cb4d3eSLandon J. Fuller
69077cb4d3eSLandon J. Fuller	# Write layout entry
69177cb4d3eSLandon J. Fuller	emit("{\n")
69277cb4d3eSLandon J. Fuller	output_depth++
69377cb4d3eSLandon J. Fuller	emit(".size = "_size",\n")
69477cb4d3eSLandon J. Fuller	emit(".rev = "revision",\n")
69577cb4d3eSLandon J. Fuller
69677cb4d3eSLandon J. Fuller	if (array_size(_flags) > 0) {
69777cb4d3eSLandon J. Fuller		emit(".flags = " array_join(_flags, "|") ",\n")
69877cb4d3eSLandon J. Fuller	} else {
69977cb4d3eSLandon J. Fuller		emit(".flags = 0,\n")
70077cb4d3eSLandon J. Fuller	}
70177cb4d3eSLandon J. Fuller
70277cb4d3eSLandon J. Fuller	emit(".srev_offset = " _rev_off ",\n")
70377cb4d3eSLandon J. Fuller
70477cb4d3eSLandon J. Fuller	if (_sromsig != null) {
70577cb4d3eSLandon J. Fuller		emit(".magic_offset = " _sig_offset ",\n")
70677cb4d3eSLandon J. Fuller		emit(".magic_value = " _sig_value ",\n")
70777cb4d3eSLandon J. Fuller	} else {
70877cb4d3eSLandon J. Fuller		emit(".magic_offset = 0,\n")
70977cb4d3eSLandon J. Fuller		emit(".magic_value = 0,\n")
71077cb4d3eSLandon J. Fuller	}
71177cb4d3eSLandon J. Fuller
712c283839dSLandon J. Fuller	emit(".crc_offset = " _crc_off ",\n")
713c283839dSLandon J. Fuller
71477cb4d3eSLandon J. Fuller	emit(".bindings = " srom_layout_get_variable_name(layout) ",\n")
71577cb4d3eSLandon J. Fuller	emit(".bindings_size = nitems(" \
71677cb4d3eSLandon J. Fuller	    srom_layout_get_variable_name(layout) "),\n")
71777cb4d3eSLandon J. Fuller
71877cb4d3eSLandon J. Fuller	emit(".num_vars = " srom_layout_num_output_vars(layout, revision) ",\n")
71977cb4d3eSLandon J. Fuller
72077cb4d3eSLandon J. Fuller	obj_delete(_flags)
72177cb4d3eSLandon J. Fuller
72277cb4d3eSLandon J. Fuller	output_depth--
72377cb4d3eSLandon J. Fuller	emit("},\n");
72477cb4d3eSLandon J. Fuller}
72577cb4d3eSLandon J. Fuller
72677cb4d3eSLandon J. Fuller# Create a new opstream encoding state instance for the given layout
72777cb4d3eSLandon J. Fullerfunction srom_ops_new(layout, _obj) {
72877cb4d3eSLandon J. Fuller	obj_assert_class(layout, SromLayout)
72977cb4d3eSLandon J. Fuller
73077cb4d3eSLandon J. Fuller	_obj = obj_new(SromOpStream)
73177cb4d3eSLandon J. Fuller	set(_obj, p_layout, layout)
73277cb4d3eSLandon J. Fuller	set(_obj, p_revisions, get(layout, p_revisions))
73377cb4d3eSLandon J. Fuller	set(_obj, p_vid, 0)
73477cb4d3eSLandon J. Fuller	set(_obj, p_offset, 0)
73577cb4d3eSLandon J. Fuller	set(_obj, p_type, null)
73677cb4d3eSLandon J. Fuller	set(_obj, p_mask, null)
73777cb4d3eSLandon J. Fuller	set(_obj, p_shift, null)
73877cb4d3eSLandon J. Fuller
73977cb4d3eSLandon J. Fuller	return (_obj)
74077cb4d3eSLandon J. Fuller}
74177cb4d3eSLandon J. Fuller
74277cb4d3eSLandon J. Fuller# Return the current type width, or throw an error if no type is currently
74377cb4d3eSLandon J. Fuller# specified.
74477cb4d3eSLandon J. Fullerfunction srom_ops_get_type_width(opstream, _type)
74577cb4d3eSLandon J. Fuller{
74677cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
74777cb4d3eSLandon J. Fuller
74877cb4d3eSLandon J. Fuller	_type = get(opstream, p_type)
74977cb4d3eSLandon J. Fuller	if (_type == null)
75077cb4d3eSLandon J. Fuller		errorx("no type value set")
75177cb4d3eSLandon J. Fuller
75277cb4d3eSLandon J. Fuller	return (get(type_get_base(_type), p_width))
75377cb4d3eSLandon J. Fuller}
75477cb4d3eSLandon J. Fuller
75577cb4d3eSLandon J. Fuller# Write a string to the SROM opcode stream, either buffering the write,
75677cb4d3eSLandon J. Fuller# or emitting it directly.
75777cb4d3eSLandon J. Fullerfunction srom_ops_emit(opstream, string, _pending_bind, _buffer) {
75877cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
75977cb4d3eSLandon J. Fuller
76077cb4d3eSLandon J. Fuller	# Buffered?
76177cb4d3eSLandon J. Fuller	if ((_pending_bind = get(opstream, p_pending_bind)) != null) {
76277cb4d3eSLandon J. Fuller		_buffer = get(_pending_bind, p_buffer)
76377cb4d3eSLandon J. Fuller		array_append(_buffer, string)
76477cb4d3eSLandon J. Fuller		return
76577cb4d3eSLandon J. Fuller	}
76677cb4d3eSLandon J. Fuller
76777cb4d3eSLandon J. Fuller	# Emit directly
76877cb4d3eSLandon J. Fuller	emit(string)
76977cb4d3eSLandon J. Fuller}
77077cb4d3eSLandon J. Fuller
77177cb4d3eSLandon J. Fuller# Emit a SROM opcode followed by up to four optional bytes
77277cb4d3eSLandon J. Fullerfunction srom_ops_emit_opcode(opstream, opcode, arg0, arg1, arg2, arg3) {
77377cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
77477cb4d3eSLandon J. Fuller
77577cb4d3eSLandon J. Fuller	srom_ops_emit(opstream, opcode",\n")
77677cb4d3eSLandon J. Fuller	if (arg0 != "") srom_ops_emit(opstream, arg0",\n")
77777cb4d3eSLandon J. Fuller	if (arg1 != "") srom_ops_emit(opstream, arg1",\n")
77877cb4d3eSLandon J. Fuller	if (arg2 != "") srom_ops_emit(opstream, arg2",\n")
77977cb4d3eSLandon J. Fuller	if (arg3 != "") srom_ops_emit(opstream, arg3",\n")
78077cb4d3eSLandon J. Fuller}
78177cb4d3eSLandon J. Fuller
78277cb4d3eSLandon J. Fuller# Emit a SROM opcode and associated integer value, choosing the best
78377cb4d3eSLandon J. Fuller# SROM_OP_DATA variant for encoding the value.
78477cb4d3eSLandon J. Fuller#
78577cb4d3eSLandon J. Fuller# opc:		The standard opcode for non-IMM encoded data, or null if none
78677cb4d3eSLandon J. Fuller# opc_imm:	The IMM opcode, or null if none
78777cb4d3eSLandon J. Fuller# value:	The value to encode
78877cb4d3eSLandon J. Fuller# svalue:	Symbolic representation of value to include in output, or null
78977cb4d3eSLandon J. Fullerfunction srom_ops_emit_int_opcode(opstream, opc, opc_imm, value, svalue,
79077cb4d3eSLandon J. Fuller    _width, _offset, _delta)
79177cb4d3eSLandon J. Fuller{
79277cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
79377cb4d3eSLandon J. Fuller
79477cb4d3eSLandon J. Fuller	# Fetch current type width
79577cb4d3eSLandon J. Fuller	_width = srom_ops_get_type_width(opstream)
79677cb4d3eSLandon J. Fuller
79777cb4d3eSLandon J. Fuller	# Special cases:
79877cb4d3eSLandon J. Fuller	if (opc_imm == SPROM_OPCODE_SHIFT_IMM) {
79977cb4d3eSLandon J. Fuller		# SHIFT_IMM -- the imm value must be positive and divisible by
80077cb4d3eSLandon J. Fuller		# two (shift/2) to use the IMM form.
80177cb4d3eSLandon J. Fuller		if (value >= 0 && value % 2 == 0) {
80277cb4d3eSLandon J. Fuller			value = (value/2)
80377cb4d3eSLandon J. Fuller			opc = null
80477cb4d3eSLandon J. Fuller		} else {
80577cb4d3eSLandon J. Fuller			opc_imm = null
80677cb4d3eSLandon J. Fuller		}
80777cb4d3eSLandon J. Fuller	} else if (opc_imm == SPROM_OPCODE_OFFSET_REL_IMM) {
80877cb4d3eSLandon J. Fuller		# OFFSET_REL_IMM -- the imm value must be positive, divisible
80977cb4d3eSLandon J. Fuller		# by the type width, and relative to the last offset to use
81077cb4d3eSLandon J. Fuller		# the IMM form.
81177cb4d3eSLandon J. Fuller
81277cb4d3eSLandon J. Fuller		# Assert that callers correctly flushed any pending bind before
81377cb4d3eSLandon J. Fuller		# attempting to set a relative offset
81477cb4d3eSLandon J. Fuller		if (get(opstream, p_pending_bind) != null)
81577cb4d3eSLandon J. Fuller			errorx("can't set relative offset with a pending bind")
81677cb4d3eSLandon J. Fuller
81777cb4d3eSLandon J. Fuller		# Fetch current offset, calculate relative value and determine
81877cb4d3eSLandon J. Fuller		# whether we can issue an IMM opcode
81977cb4d3eSLandon J. Fuller		_offset = get(opstream, p_offset)
82077cb4d3eSLandon J. Fuller		_delta = value - _offset
82177cb4d3eSLandon J. Fuller		if (_delta >= 0 &&
82277cb4d3eSLandon J. Fuller		    _delta % _width == 0 &&
82377cb4d3eSLandon J. Fuller		    (_delta/_width) <= SPROM_OP_IMM_MAX)
82477cb4d3eSLandon J. Fuller		{
82577cb4d3eSLandon J. Fuller			srom_ops_emit(opstream,
82677cb4d3eSLandon J. Fuller			    sprintf("/* %#x + %#x -> %#x */\n", _offset,
82777cb4d3eSLandon J. Fuller			    _delta, value))
82877cb4d3eSLandon J. Fuller			value = (_delta / _width)
82977cb4d3eSLandon J. Fuller			opc = null
83077cb4d3eSLandon J. Fuller		} else {
83177cb4d3eSLandon J. Fuller			opc_imm = null
83277cb4d3eSLandon J. Fuller		}
83377cb4d3eSLandon J. Fuller	}
83477cb4d3eSLandon J. Fuller
83577cb4d3eSLandon J. Fuller	# If no symbolic representation provided, write the raw value
83677cb4d3eSLandon J. Fuller	if (svalue == null)
83777cb4d3eSLandon J. Fuller		svalue = value
83877cb4d3eSLandon J. Fuller
83977cb4d3eSLandon J. Fuller	# Try to encode as IMM value?
84077cb4d3eSLandon J. Fuller	if (opc_imm != null && value >= 0 && value <= SPROM_OP_IMM_MAX) {
84177cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream, "("opc_imm"|"svalue")")
84277cb4d3eSLandon J. Fuller		return
84377cb4d3eSLandon J. Fuller	}
84477cb4d3eSLandon J. Fuller
84577cb4d3eSLandon J. Fuller	# Can't encode as immediate; do we have a non-immediate form?
84677cb4d3eSLandon J. Fuller	if (opc == null)
84777cb4d3eSLandon J. Fuller		errorx("can't encode '" value "' as immediate, and no " \
84877cb4d3eSLandon J. Fuller		    "non-immediate form was provided")
84977cb4d3eSLandon J. Fuller
85077cb4d3eSLandon J. Fuller	# Determine and emit minimal encoding
85177cb4d3eSLandon J. Fuller	# We let the C compiler perform the bit operations, rather than
85277cb4d3eSLandon J. Fuller	# trying to wrestle awk's floating point arithmetic
85377cb4d3eSLandon J. Fuller	if (value < 0) {
85477cb4d3eSLandon J. Fuller		# Only Int8 is used
85577cb4d3eSLandon J. Fuller		 if (value < Int8Min)
85677cb4d3eSLandon J. Fuller			 errorx("cannot int8 encode '" value "'")
85777cb4d3eSLandon J. Fuller
85877cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream,
85977cb4d3eSLandon J. Fuller		    "("opc"|"SPROM_OP_DATA_I8")", svalue)
86077cb4d3eSLandon J. Fuller
86177cb4d3eSLandon J. Fuller	} else if (value <= UInt8Max) {
86277cb4d3eSLandon J. Fuller
86377cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream,
86477cb4d3eSLandon J. Fuller		    "("opc"|"SPROM_OP_DATA_U8")", svalue)
86577cb4d3eSLandon J. Fuller
86677cb4d3eSLandon J. Fuller	} else if (value % _width == 0 && (value / _width) <= UInt8Max) {
86777cb4d3eSLandon J. Fuller
86877cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream,
86977cb4d3eSLandon J. Fuller		    "("opc"|"SPROM_OP_DATA_U8_SCALED")", svalue / _width)
87077cb4d3eSLandon J. Fuller
87177cb4d3eSLandon J. Fuller	} else if (value <= UInt16Max) {
87277cb4d3eSLandon J. Fuller
87377cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream,
87477cb4d3eSLandon J. Fuller		    "("opc"|"SPROM_OP_DATA_U16")",
87577cb4d3eSLandon J. Fuller		    "("svalue" & 0xFF)",
87677cb4d3eSLandon J. Fuller		    "("svalue" >> 8)")
87777cb4d3eSLandon J. Fuller
87877cb4d3eSLandon J. Fuller	} else if (value <= UInt32Max) {
87977cb4d3eSLandon J. Fuller
88077cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream,
88177cb4d3eSLandon J. Fuller		    "("opc"|"SPROM_OP_DATA_U32")",
88277cb4d3eSLandon J. Fuller		    "("svalue" & 0xFF)",
88377cb4d3eSLandon J. Fuller		    "(("svalue" >> 8) & 0xFF)",
88477cb4d3eSLandon J. Fuller		    "(("svalue" >> 16) & 0xFF)",
88577cb4d3eSLandon J. Fuller		    "(("svalue" >> 24) & 0xFF)")
88677cb4d3eSLandon J. Fuller
88777cb4d3eSLandon J. Fuller	} else {
88877cb4d3eSLandon J. Fuller		errorx("can't encode '" value "' (too large)")
88977cb4d3eSLandon J. Fuller	}
89077cb4d3eSLandon J. Fuller}
89177cb4d3eSLandon J. Fuller
89277cb4d3eSLandon J. Fuller# Emit initial OPCODE_VAR opcode and update opstream state
89377cb4d3eSLandon J. Fullerfunction srom_ops_reset_var(opstream, var, _vid_prev, _vid, _vid_name,
89477cb4d3eSLandon J. Fuller    _type, _base_type)
89577cb4d3eSLandon J. Fuller{
89677cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
89777cb4d3eSLandon J. Fuller	obj_assert_class(var, Var)
89877cb4d3eSLandon J. Fuller
89977cb4d3eSLandon J. Fuller	# Flush any pending bind for the previous variable
90077cb4d3eSLandon J. Fuller	srom_ops_flush_bind(opstream, 1)
90177cb4d3eSLandon J. Fuller
90277cb4d3eSLandon J. Fuller	# Fetch current state
90377cb4d3eSLandon J. Fuller	_vid_prev = get(opstream, p_vid)
90477cb4d3eSLandon J. Fuller
90577cb4d3eSLandon J. Fuller	_vid = get(var, p_vid)
90677cb4d3eSLandon J. Fuller	_vid_name = var_get_macro_name(var, MTypeVarID)
90777cb4d3eSLandon J. Fuller
90877cb4d3eSLandon J. Fuller	# Update state
90977cb4d3eSLandon J. Fuller	_type = get(var, p_type)
91077cb4d3eSLandon J. Fuller	set(opstream, p_vid, _vid)
91177cb4d3eSLandon J. Fuller	set(opstream, p_type, type_get_base(_type))
91277cb4d3eSLandon J. Fuller	set(opstream, p_nelem, var_get_array_len(var))
91377cb4d3eSLandon J. Fuller	set(opstream, p_mask, type_get_default_mask(_type))
91477cb4d3eSLandon J. Fuller	set(opstream, p_shift, 0)
91577cb4d3eSLandon J. Fuller	set(opstream, p_bind_total, 0)
91677cb4d3eSLandon J. Fuller
91777cb4d3eSLandon J. Fuller	# Always provide a human readable comment
91877cb4d3eSLandon J. Fuller	srom_ops_emit(opstream, sprintf("/* %s (%#x) */\n", get(var, p_name),
91977cb4d3eSLandon J. Fuller	    get(opstream, p_offset)))
92077cb4d3eSLandon J. Fuller
92177cb4d3eSLandon J. Fuller	# Prefer a single VAR_IMM byte
92277cb4d3eSLandon J. Fuller	if (_vid_prev == 0 || _vid <= SPROM_OP_IMM_MAX) {
92377cb4d3eSLandon J. Fuller		srom_ops_emit_int_opcode(opstream,
92477cb4d3eSLandon J. Fuller		    null, SPROM_OPCODE_VAR_IMM,
92577cb4d3eSLandon J. Fuller		    _vid, _vid_name)
92677cb4d3eSLandon J. Fuller		return
92777cb4d3eSLandon J. Fuller	}
92877cb4d3eSLandon J. Fuller
92977cb4d3eSLandon J. Fuller	# Try encoding as a single VAR_REL_IMM byte
93077cb4d3eSLandon J. Fuller	if (_vid_prev <= _vid && (_vid - _vid_prev) <= SPROM_OP_IMM_MAX) {
93177cb4d3eSLandon J. Fuller		srom_ops_emit_int_opcode(opstream,
93277cb4d3eSLandon J. Fuller		    null, SPROM_OPCODE_VAR_REL_IMM,
93377cb4d3eSLandon J. Fuller		    _vid - _vid_prev, null)
93477cb4d3eSLandon J. Fuller		return
93577cb4d3eSLandon J. Fuller	}
93677cb4d3eSLandon J. Fuller
93777cb4d3eSLandon J. Fuller	# Fall back on a multibyte encoding
93877cb4d3eSLandon J. Fuller	srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_VAR, null, _vid,
93977cb4d3eSLandon J. Fuller	    _vid_name)
94077cb4d3eSLandon J. Fuller}
94177cb4d3eSLandon J. Fuller
94277cb4d3eSLandon J. Fuller# Emit OPCODE_REV/OPCODE_REV_RANGE (if necessary) for a new revision range
94377cb4d3eSLandon J. Fullerfunction srom_ops_emit_revisions(opstream, revisions, _prev_revs,
94477cb4d3eSLandon J. Fuller    _start, _end)
94577cb4d3eSLandon J. Fuller{
94677cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
94777cb4d3eSLandon J. Fuller	_prev_revs = get(opstream, p_revisions)
94877cb4d3eSLandon J. Fuller
94977cb4d3eSLandon J. Fuller	if (revrange_equal(_prev_revs, revisions))
95077cb4d3eSLandon J. Fuller		return;
95177cb4d3eSLandon J. Fuller
95277cb4d3eSLandon J. Fuller	# Update stream state
95377cb4d3eSLandon J. Fuller	set(opstream, p_revisions, revisions)
95477cb4d3eSLandon J. Fuller
95577cb4d3eSLandon J. Fuller	_start = get(revisions, p_start)
95677cb4d3eSLandon J. Fuller	_end = get(revisions, p_end)
95777cb4d3eSLandon J. Fuller
95877cb4d3eSLandon J. Fuller	# Sanity-check range values
95977cb4d3eSLandon J. Fuller	if (_start < 0 || _end < 0)
96077cb4d3eSLandon J. Fuller		errorx("invalid range: " revrange_to_string(revisions))
96177cb4d3eSLandon J. Fuller
96277cb4d3eSLandon J. Fuller	# If range covers a single revision, and can be encoded within
96377cb4d3eSLandon J. Fuller	# SROM_OP_IMM_MAX, we can use the single byte encoding
96477cb4d3eSLandon J. Fuller	if (_start == _end && _start <= SPROM_OP_IMM_MAX) {
96577cb4d3eSLandon J. Fuller		srom_ops_emit_int_opcode(opstream,
96677cb4d3eSLandon J. Fuller		    null, SPROM_OPCODE_REV_IMM, _start)
96777cb4d3eSLandon J. Fuller		return
96877cb4d3eSLandon J. Fuller	}
96977cb4d3eSLandon J. Fuller
97077cb4d3eSLandon J. Fuller	# Otherwise, we need to use the two byte range encoding
97177cb4d3eSLandon J. Fuller	if (_start > SPROM_OP_REV_RANGE_MAX || _end > SPROM_OP_REV_RANGE_MAX) {
97277cb4d3eSLandon J. Fuller		errorx(sprintf("cannot encode range values %s (>= %u)",
97377cb4d3eSLandon J. Fuller		    revrange_to_string(revisions), SPROM_OP_REV_RANGE_MAX))
97477cb4d3eSLandon J. Fuller	}
97577cb4d3eSLandon J. Fuller
97677cb4d3eSLandon J. Fuller	srom_ops_emit_opcode(opstream,
97777cb4d3eSLandon J. Fuller	    SPROM_OPCODE_REV_RANGE,
97877cb4d3eSLandon J. Fuller	    sprintf("(%u << %s) | (%u << %s)",
97977cb4d3eSLandon J. Fuller		_start, SPROM_OP_REV_START_SHIFT,
98077cb4d3eSLandon J. Fuller		_end, SPROM_OP_REV_END_SHIFT))
98177cb4d3eSLandon J. Fuller}
98277cb4d3eSLandon J. Fuller
98377cb4d3eSLandon J. Fuller# Emit OPCODE_OFFSET (if necessary) for a new offset
98477cb4d3eSLandon J. Fullerfunction srom_ops_emit_offset(opstream, offset, _prev_offset, _rel_offset,
98577cb4d3eSLandon J. Fuller    _bind)
98677cb4d3eSLandon J. Fuller{
98777cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
98877cb4d3eSLandon J. Fuller
98977cb4d3eSLandon J. Fuller	# Flush any pending bind before adjusting the offset
99077cb4d3eSLandon J. Fuller	srom_ops_flush_bind(opstream, 0)
99177cb4d3eSLandon J. Fuller
99277cb4d3eSLandon J. Fuller	# Fetch current offset
99377cb4d3eSLandon J. Fuller	_prev_offset = get(opstream, p_offset)
99477cb4d3eSLandon J. Fuller	if (_prev_offset == offset)
99577cb4d3eSLandon J. Fuller		return
99677cb4d3eSLandon J. Fuller
99777cb4d3eSLandon J. Fuller	# Encode (possibly a relative, 1-byte form) of the offset opcode
99877cb4d3eSLandon J. Fuller	srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_OFFSET,
99977cb4d3eSLandon J. Fuller	    SPROM_OPCODE_OFFSET_REL_IMM, offset, null)
100077cb4d3eSLandon J. Fuller
100177cb4d3eSLandon J. Fuller	# Update state
100277cb4d3eSLandon J. Fuller	set(opstream, p_offset, offset)
100377cb4d3eSLandon J. Fuller}
100477cb4d3eSLandon J. Fuller
100577cb4d3eSLandon J. Fuller# Emit OPCODE_TYPE (if necessary) for a new type value; this also
100677cb4d3eSLandon J. Fuller# resets the mask to the type default.
100777cb4d3eSLandon J. Fullerfunction srom_ops_emit_type(opstream, type, _base_type, _prev_type, _prev_mask,
100877cb4d3eSLandon J. Fuller    _default_mask)
100977cb4d3eSLandon J. Fuller{
101077cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
101177cb4d3eSLandon J. Fuller	if (!obj_is_instanceof(type, ArrayType))
101277cb4d3eSLandon J. Fuller		obj_assert_class(type, Type)
101377cb4d3eSLandon J. Fuller
101477cb4d3eSLandon J. Fuller	_default_mask = type_get_default_mask(type)
101577cb4d3eSLandon J. Fuller	_base_type = type_get_base(type)
101677cb4d3eSLandon J. Fuller
101777cb4d3eSLandon J. Fuller	# If state already matches the requested type, nothing to be
101877cb4d3eSLandon J. Fuller	# done
101977cb4d3eSLandon J. Fuller	_prev_type = get(opstream, p_type)
102077cb4d3eSLandon J. Fuller	_prev_mask = get(opstream, p_mask)
102177cb4d3eSLandon J. Fuller	if (type_equal(_prev_type, _base_type) && _prev_mask == _default_mask)
102277cb4d3eSLandon J. Fuller		return
102377cb4d3eSLandon J. Fuller
102477cb4d3eSLandon J. Fuller	# Update state
102577cb4d3eSLandon J. Fuller	set(opstream, p_type, _base_type)
102677cb4d3eSLandon J. Fuller	set(opstream, p_mask, _default_mask)
102777cb4d3eSLandon J. Fuller
102877cb4d3eSLandon J. Fuller	# Emit opcode.
102977cb4d3eSLandon J. Fuller	if (type_get_const_val(_base_type) <= SPROM_OP_IMM_MAX) {
103077cb4d3eSLandon J. Fuller		# Single byte IMM encoding
103177cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream,
103277cb4d3eSLandon J. Fuller		    SPROM_OPCODE_TYPE_IMM "|" type_get_const(_base_type))
103377cb4d3eSLandon J. Fuller	} else {
103477cb4d3eSLandon J. Fuller		# Two byte encoding
103577cb4d3eSLandon J. Fuller		srom_ops_emit_opcode(opstream, SPROM_OPCODE_TYPE,
103677cb4d3eSLandon J. Fuller		    type_get_const(_base_type))
103777cb4d3eSLandon J. Fuller	}
103877cb4d3eSLandon J. Fuller}
103977cb4d3eSLandon J. Fuller
104077cb4d3eSLandon J. Fuller# Emit OPCODE_MASK (if necessary) for a new mask value
104177cb4d3eSLandon J. Fullerfunction srom_ops_emit_mask(opstream, mask, _prev_mask) {
104277cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
104377cb4d3eSLandon J. Fuller	_prev_mask = get(opstream, p_mask)
104477cb4d3eSLandon J. Fuller
104577cb4d3eSLandon J. Fuller	if (_prev_mask == mask)
104677cb4d3eSLandon J. Fuller		return
104777cb4d3eSLandon J. Fuller
104877cb4d3eSLandon J. Fuller	set(opstream, p_mask, mask)
104977cb4d3eSLandon J. Fuller	srom_ops_emit_int_opcode(opstream,
105077cb4d3eSLandon J. Fuller	    SPROM_OPCODE_MASK, SPROM_OPCODE_MASK_IMM,
105177cb4d3eSLandon J. Fuller	    mask, sprintf("0x%x", mask))
105277cb4d3eSLandon J. Fuller}
105377cb4d3eSLandon J. Fuller
105477cb4d3eSLandon J. Fuller# Emit OPCODE_SHIFT (if necessary) for a new shift value
105577cb4d3eSLandon J. Fullerfunction srom_ops_emit_shift(opstream, shift, _prev_shift) {
105677cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
105777cb4d3eSLandon J. Fuller	_prev_shift = get(opstream, p_shift)
105877cb4d3eSLandon J. Fuller
105977cb4d3eSLandon J. Fuller	if (_prev_shift == shift)
106077cb4d3eSLandon J. Fuller		return
106177cb4d3eSLandon J. Fuller
106277cb4d3eSLandon J. Fuller	set(opstream, p_shift, shift)
106377cb4d3eSLandon J. Fuller	srom_ops_emit_int_opcode(opstream,
106477cb4d3eSLandon J. Fuller	    SPROM_OPCODE_SHIFT, SPROM_OPCODE_SHIFT_IMM,
106577cb4d3eSLandon J. Fuller	    shift, null)
106677cb4d3eSLandon J. Fuller}
106777cb4d3eSLandon J. Fuller
106877cb4d3eSLandon J. Fuller# Return true if a valid BIND/BINDN encoding exists for the given SKIP_IN
106977cb4d3eSLandon J. Fuller# value, false if the skip values exceed the limits of the bind opcode
107077cb4d3eSLandon J. Fuller# family.
107177cb4d3eSLandon J. Fullerfunction srom_ops_can_encode_skip_in(skip_in) {
107277cb4d3eSLandon J. Fuller	return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
107377cb4d3eSLandon J. Fuller	    skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
107477cb4d3eSLandon J. Fuller}
107577cb4d3eSLandon J. Fuller
107677cb4d3eSLandon J. Fuller# Return true if a valid BIND/BINDN encoding exists for the given SKIP_OUT
107777cb4d3eSLandon J. Fuller# value, false if the skip values exceed the limits of the bind opcode
107877cb4d3eSLandon J. Fuller# family.
107977cb4d3eSLandon J. Fullerfunction srom_ops_can_encode_skip_out(skip_out) {
108077cb4d3eSLandon J. Fuller	return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
108177cb4d3eSLandon J. Fuller	    skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
108277cb4d3eSLandon J. Fuller}
108377cb4d3eSLandon J. Fuller
108477cb4d3eSLandon J. Fuller# Return true if a valid BIND/BINDN encoding exists for the given skip
108577cb4d3eSLandon J. Fuller# values, false if the skip values exceed the limits of the bind opcode
108677cb4d3eSLandon J. Fuller# family.
108777cb4d3eSLandon J. Fullerfunction srom_ops_can_encode_skip(skip_in, skip_out) {
108877cb4d3eSLandon J. Fuller	return (srom_ops_can_encode_skip_in(skip_in) &&
108977cb4d3eSLandon J. Fuller	    srom_ops_can_encode_skip_out(skip_out))
109077cb4d3eSLandon J. Fuller}
109177cb4d3eSLandon J. Fuller
109277cb4d3eSLandon J. Fuller# Create a new SromOpBind instance for the given segment
109377cb4d3eSLandon J. Fullerfunction srom_opbind_new(segment, skip_in, skip_out, _obj, _type, _width,
109477cb4d3eSLandon J. Fuller    _offset)
109577cb4d3eSLandon J. Fuller{
109677cb4d3eSLandon J. Fuller	obj_assert_class(segment, SromSegment)
109777cb4d3eSLandon J. Fuller
109877cb4d3eSLandon J. Fuller	# Verify that an encoding exists for the skip values
109977cb4d3eSLandon J. Fuller	if (!srom_ops_can_encode_skip_in(skip_in)) {
110077cb4d3eSLandon J. Fuller		errorx(sprintf("cannot encode SKIP_IN=%d; maximum supported " \
110177cb4d3eSLandon J. Fuller		    "range %d-%d", skip_in,
110277cb4d3eSLandon J. Fuller		    SPROM_OP_BIND_SKIP_IN_MIN, SPROM_OP_BIND_SKIP_IN_MAX))
110377cb4d3eSLandon J. Fuller	}
110477cb4d3eSLandon J. Fuller
110577cb4d3eSLandon J. Fuller	if (!srom_ops_can_encode_skip_out(skip_out)) {
110677cb4d3eSLandon J. Fuller		errorx(sprintf("cannot encode SKIP_OUT=%d; maximum supported " \
110777cb4d3eSLandon J. Fuller		    "range %d-%d", skip_out,
110877cb4d3eSLandon J. Fuller		    SPROM_OP_BIND_SKIP_OUT_MIN, SPROM_OP_BIND_SKIP_OUT_MAX))
110977cb4d3eSLandon J. Fuller	}
111077cb4d3eSLandon J. Fuller
111177cb4d3eSLandon J. Fuller	# Fetch basic segment info
111277cb4d3eSLandon J. Fuller	_offset = get(segment, p_offset)
111377cb4d3eSLandon J. Fuller	_type = srom_segment_get_base_type(segment)
111477cb4d3eSLandon J. Fuller	_width = get(_type, p_width)
111577cb4d3eSLandon J. Fuller
111677cb4d3eSLandon J. Fuller	# Construct new instance
111777cb4d3eSLandon J. Fuller	_obj = obj_new(SromOpBind)
111877cb4d3eSLandon J. Fuller
111977cb4d3eSLandon J. Fuller	set(_obj, p_segment, segment)
112077cb4d3eSLandon J. Fuller	set(_obj, p_count, 1)
112177cb4d3eSLandon J. Fuller	set(_obj, p_offset, _offset)
112277cb4d3eSLandon J. Fuller	set(_obj, p_width, _width)
112377cb4d3eSLandon J. Fuller	set(_obj, p_skip_in, skip_in)
112477cb4d3eSLandon J. Fuller	set(_obj, p_skip_out, skip_out)
112577cb4d3eSLandon J. Fuller	set(_obj, p_buffer, array_new())
112677cb4d3eSLandon J. Fuller
112777cb4d3eSLandon J. Fuller	return (_obj)
112877cb4d3eSLandon J. Fuller}
112977cb4d3eSLandon J. Fuller
113077cb4d3eSLandon J. Fuller# Try to coalesce a BIND for the given segment with an existing bind request,
113177cb4d3eSLandon J. Fuller# returning true on success, or false if the two segments cannot be coalesced
113277cb4d3eSLandon J. Fuller# into the existing request
113377cb4d3eSLandon J. Fullerfunction srom_opbind_append(bind, segment, skip_out, _bind_seg, _bind_off,
113477cb4d3eSLandon J. Fuller    _width, _count, _skip_in, _seg_offset, _delta)
113577cb4d3eSLandon J. Fuller{
113677cb4d3eSLandon J. Fuller	obj_assert_class(bind, SromOpBind)
113777cb4d3eSLandon J. Fuller	obj_assert_class(segment, SromSegment)
113877cb4d3eSLandon J. Fuller
113977cb4d3eSLandon J. Fuller	# Are the segments compatible?
114077cb4d3eSLandon J. Fuller	_bind_seg = get(bind, p_segment)
114177cb4d3eSLandon J. Fuller	if (!srom_segment_attributes_equal(_bind_seg, segment))
114277cb4d3eSLandon J. Fuller		return (0)
114377cb4d3eSLandon J. Fuller
114477cb4d3eSLandon J. Fuller	# Are the output skip values compatible?
114577cb4d3eSLandon J. Fuller	if (get(bind, p_skip_out) != skip_out)
114677cb4d3eSLandon J. Fuller		return (0)
114777cb4d3eSLandon J. Fuller
114877cb4d3eSLandon J. Fuller	# Find bind offset/count/width/skip
114977cb4d3eSLandon J. Fuller	_bind_off = get(bind, p_offset)
115077cb4d3eSLandon J. Fuller	_count = get(bind, p_count)
115177cb4d3eSLandon J. Fuller	_skip_in = get(bind, p_skip_in)
115277cb4d3eSLandon J. Fuller	_width = get(bind, p_width)
115377cb4d3eSLandon J. Fuller
115477cb4d3eSLandon J. Fuller	# Fetch new segment's offset
115577cb4d3eSLandon J. Fuller	_seg_offset = get(segment, p_offset)
115677cb4d3eSLandon J. Fuller
115777cb4d3eSLandon J. Fuller	# If there's only one segment in the bind op, we ned to compute the
115877cb4d3eSLandon J. Fuller	# skip value to be used for all later segments (including the
115977cb4d3eSLandon J. Fuller	# segment we're attempting to append)
116077cb4d3eSLandon J. Fuller	#
116177cb4d3eSLandon J. Fuller	# If there's already multiple segments, we just need to verify that
116277cb4d3eSLandon J. Fuller	# the bind_offset + (count * width * skip_in) produces the new
116377cb4d3eSLandon J. Fuller	# segment's offset
116477cb4d3eSLandon J. Fuller	if (_count == 1) {
116577cb4d3eSLandon J. Fuller		# Determine the delta between the two segment offsets. This
116677cb4d3eSLandon J. Fuller		# must be a multiple of the type width to be encoded
116777cb4d3eSLandon J. Fuller		# as a BINDN entry
116877cb4d3eSLandon J. Fuller		_delta = _seg_offset - _bind_off
116977cb4d3eSLandon J. Fuller		if ((_delta % _width) != 0)
117077cb4d3eSLandon J. Fuller			return (0)
117177cb4d3eSLandon J. Fuller
117277cb4d3eSLandon J. Fuller		# The skip byte count is calculated as (type width * skip)
117377cb4d3eSLandon J. Fuller		_skip_in = _delta / _width
117477cb4d3eSLandon J. Fuller
117577cb4d3eSLandon J. Fuller		# Is the skip encodable?
117677cb4d3eSLandon J. Fuller		if (!srom_ops_can_encode_skip_in(_skip_in))
117777cb4d3eSLandon J. Fuller			return (0)
117877cb4d3eSLandon J. Fuller
117977cb4d3eSLandon J. Fuller		# Save required skip
118077cb4d3eSLandon J. Fuller		set(bind, p_skip_in, _skip_in)
118177cb4d3eSLandon J. Fuller	} else if (_count > 1) {
118277cb4d3eSLandon J. Fuller		# Get the final offset of the binding if we were to add
118377cb4d3eSLandon J. Fuller		# one additional segment
118477cb4d3eSLandon J. Fuller		_bind_off = _bind_off + (_width * _skip_in * (_count + 1))
118577cb4d3eSLandon J. Fuller
118677cb4d3eSLandon J. Fuller		# If it doesn't match our segment's offset, we can't
118777cb4d3eSLandon J. Fuller		# append this segment
118877cb4d3eSLandon J. Fuller		if (_bind_off != _seg_offset)
118977cb4d3eSLandon J. Fuller			return (0)
119077cb4d3eSLandon J. Fuller	}
119177cb4d3eSLandon J. Fuller
119277cb4d3eSLandon J. Fuller	# Success! Increment the bind count in the existing bind
119377cb4d3eSLandon J. Fuller	set(bind, p_count, _count + 1)
119477cb4d3eSLandon J. Fuller	return (1)
119577cb4d3eSLandon J. Fuller}
119677cb4d3eSLandon J. Fuller
119777cb4d3eSLandon J. Fuller# Return true if the given binding operation can be omitted from the output
119877cb4d3eSLandon J. Fuller# if it would be immediately followed by a VAR, VAR_REL_IMM, or EOF opcode.
119977cb4d3eSLandon J. Fuller#
120077cb4d3eSLandon J. Fuller# The bind operatin must be configured with default count, skip_in, and
120177cb4d3eSLandon J. Fuller# skip_out values of 1, and must contain no buffered post-BIND opcodes
120277cb4d3eSLandon J. Fullerfunction srom_opbind_is_implicit_encodable(bind) {
120377cb4d3eSLandon J. Fuller	obj_assert_class(bind, SromOpBind)
120477cb4d3eSLandon J. Fuller
120577cb4d3eSLandon J. Fuller	if (get(bind, p_count) != 1)
120677cb4d3eSLandon J. Fuller		return (0)
120777cb4d3eSLandon J. Fuller
120877cb4d3eSLandon J. Fuller	if (get(bind, p_skip_in) != 1)
120977cb4d3eSLandon J. Fuller		return (0)
121077cb4d3eSLandon J. Fuller
121177cb4d3eSLandon J. Fuller	if (get(bind, p_skip_out) != 1)
121277cb4d3eSLandon J. Fuller		return (0)
121377cb4d3eSLandon J. Fuller
121477cb4d3eSLandon J. Fuller	if (array_size(get(bind, p_buffer)) != 0)
121577cb4d3eSLandon J. Fuller		return (0)
121677cb4d3eSLandon J. Fuller
121777cb4d3eSLandon J. Fuller	return (1)
121877cb4d3eSLandon J. Fuller}
121977cb4d3eSLandon J. Fuller
122077cb4d3eSLandon J. Fuller
122177cb4d3eSLandon J. Fuller# Encode all segment settings for a single offset segment, followed by a bind
122277cb4d3eSLandon J. Fuller# request.
122377cb4d3eSLandon J. Fuller#
122477cb4d3eSLandon J. Fuller# opstream:	Opcode stream
122577cb4d3eSLandon J. Fuller# segment:	Segment to be written
122677cb4d3eSLandon J. Fuller# continued:	If this segment's value should be OR'd with the value of a
122777cb4d3eSLandon J. Fuller#		following segment
122877cb4d3eSLandon J. Fullerfunction srom_ops_emit_segment(opstream, segment, continued, _value,
122977cb4d3eSLandon J. Fuller    _bind, _skip_in, _skip_out)
123077cb4d3eSLandon J. Fuller{
123177cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
123277cb4d3eSLandon J. Fuller	obj_assert_class(segment, SromSegment)
123377cb4d3eSLandon J. Fuller
123477cb4d3eSLandon J. Fuller	# Determine basic bind parameters
123577cb4d3eSLandon J. Fuller	_count = 1
123677cb4d3eSLandon J. Fuller	_skip_in = 1
123777cb4d3eSLandon J. Fuller	_skip_out = continued ? 0 : 1
123877cb4d3eSLandon J. Fuller
123977cb4d3eSLandon J. Fuller	# Try to coalesce with a pending binding
124077cb4d3eSLandon J. Fuller	if ((_bind = get(opstream, p_pending_bind)) != null) {
124177cb4d3eSLandon J. Fuller		if (srom_opbind_append(_bind, segment, _skip_out))
124277cb4d3eSLandon J. Fuller			return
124377cb4d3eSLandon J. Fuller	}
124477cb4d3eSLandon J. Fuller
124577cb4d3eSLandon J. Fuller	# Otherwise, flush any pending bind and enqueue our own
124677cb4d3eSLandon J. Fuller	srom_ops_flush_bind(opstream, 0)
124777cb4d3eSLandon J. Fuller	if (get(opstream, p_pending_bind))
124877cb4d3eSLandon J. Fuller		errorx("bind not flushed!")
124977cb4d3eSLandon J. Fuller
125077cb4d3eSLandon J. Fuller	# Encode type
125177cb4d3eSLandon J. Fuller	_value = get(segment, p_type)
125277cb4d3eSLandon J. Fuller	srom_ops_emit_type(opstream, _value)
125377cb4d3eSLandon J. Fuller
125477cb4d3eSLandon J. Fuller	# Encode offset
125577cb4d3eSLandon J. Fuller	_value = get(segment, p_offset)
125677cb4d3eSLandon J. Fuller	srom_ops_emit_offset(opstream, _value)
125777cb4d3eSLandon J. Fuller
125877cb4d3eSLandon J. Fuller	# Encode mask
125977cb4d3eSLandon J. Fuller	_value = get(segment, p_mask)
126077cb4d3eSLandon J. Fuller	srom_ops_emit_mask(opstream, _value)
126177cb4d3eSLandon J. Fuller
126277cb4d3eSLandon J. Fuller	# Encode shift
126377cb4d3eSLandon J. Fuller	_value = get(segment, p_shift)
126477cb4d3eSLandon J. Fuller	srom_ops_emit_shift(opstream, _value)
126577cb4d3eSLandon J. Fuller
126677cb4d3eSLandon J. Fuller	# Enqueue binding with opstream
126777cb4d3eSLandon J. Fuller	_bind = srom_opbind_new(segment, _skip_in, _skip_out)
126877cb4d3eSLandon J. Fuller	set(opstream, p_pending_bind, _bind)
126977cb4d3eSLandon J. Fuller}
127077cb4d3eSLandon J. Fuller
127177cb4d3eSLandon J. Fuller# (private) Adjust the stream's input offset by applying the given bind
127277cb4d3eSLandon J. Fuller# operation's skip_in * width * count.
127377cb4d3eSLandon J. Fullerfunction _srom_ops_apply_bind_offset(opstream, bind, _count, _offset, _width,
127477cb4d3eSLandon J. Fuller    _skip_in, _opstream_offset)
127577cb4d3eSLandon J. Fuller{
127677cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
127777cb4d3eSLandon J. Fuller	obj_assert_class(bind, SromOpBind)
127877cb4d3eSLandon J. Fuller
127977cb4d3eSLandon J. Fuller	_opstream_offset = get(opstream, p_offset)
128077cb4d3eSLandon J. Fuller	_offset = get(bind, p_offset)
128177cb4d3eSLandon J. Fuller	if (_opstream_offset != _offset)
128277cb4d3eSLandon J. Fuller		errorx("stream/bind offset state mismatch")
128377cb4d3eSLandon J. Fuller
128477cb4d3eSLandon J. Fuller	_count = get(bind, p_count)
128577cb4d3eSLandon J. Fuller	_width = get(bind, p_width)
128677cb4d3eSLandon J. Fuller	_skip_in = get(bind, p_skip_in)
128777cb4d3eSLandon J. Fuller
128877cb4d3eSLandon J. Fuller	set(opstream, p_offset,
128977cb4d3eSLandon J. Fuller	    _opstream_offset + ((_width * _skip_in) * _count))
129077cb4d3eSLandon J. Fuller}
129177cb4d3eSLandon J. Fuller
129277cb4d3eSLandon J. Fuller# (private) Write a bind instance and all buffered opcodes
129377cb4d3eSLandon J. Fullerfunction _srom_ops_emit_bind(opstream, bind, _count, _skip_in, _skip_out,
129477cb4d3eSLandon J. Fuller    _off_start, _width, _si_signbit, _written, _nbuffer, _buffer)
129577cb4d3eSLandon J. Fuller{
129677cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
129777cb4d3eSLandon J. Fuller	obj_assert_class(bind, SromOpBind)
129877cb4d3eSLandon J. Fuller
129977cb4d3eSLandon J. Fuller	# Assert that any pending bind state has already been cleared
130077cb4d3eSLandon J. Fuller	if (get(opstream, p_pending_bind) != null)
130177cb4d3eSLandon J. Fuller		errorx("cannot flush bind with an existing pending_bind active")
130277cb4d3eSLandon J. Fuller
130377cb4d3eSLandon J. Fuller	# Fetch (and assert valid) our skip values
130477cb4d3eSLandon J. Fuller	_skip_in = get(bind, p_skip_in)
130577cb4d3eSLandon J. Fuller	_skip_out = get(bind, p_skip_out)
130677cb4d3eSLandon J. Fuller
130777cb4d3eSLandon J. Fuller	if (!srom_ops_can_encode_skip(_skip_in, _skip_out))
130877cb4d3eSLandon J. Fuller		errorx("invalid skip values in buffered bind")
130977cb4d3eSLandon J. Fuller
131077cb4d3eSLandon J. Fuller	# Determine SKIP_IN sign bit
131177cb4d3eSLandon J. Fuller	_si_signbit = "0"
131277cb4d3eSLandon J. Fuller	if (_skip_in < 0)
131377cb4d3eSLandon J. Fuller		_si_signbit = SPROM_OP_BIND_SKIP_IN_SIGN
131477cb4d3eSLandon J. Fuller
131577cb4d3eSLandon J. Fuller	# Emit BIND/BINDN opcodes until the full count is encoded
131677cb4d3eSLandon J. Fuller	_count = get(bind, p_count)
131777cb4d3eSLandon J. Fuller	while (_count > 0) {
131877cb4d3eSLandon J. Fuller		if (_count > 1 && _count <= SPROM_OP_IMM_MAX &&
131977cb4d3eSLandon J. Fuller		    _skip_in == 1 && _skip_out == 1)
132077cb4d3eSLandon J. Fuller		{
132177cb4d3eSLandon J. Fuller			# The one-byte BINDN form requires encoding the count
132277cb4d3eSLandon J. Fuller			# as a IMM, and has an implicit in/out skip of 1.
132377cb4d3eSLandon J. Fuller			srom_ops_emit_opcode(opstream,
132477cb4d3eSLandon J. Fuller			    "("SPROM_OPCODE_DO_BINDN_IMM"|"_count")")
132577cb4d3eSLandon J. Fuller			_count -= _count
132677cb4d3eSLandon J. Fuller
132777cb4d3eSLandon J. Fuller		} else if (_count > 1) {
132877cb4d3eSLandon J. Fuller			# The two byte BINDN form can encode skip values and a
132977cb4d3eSLandon J. Fuller			# larger U8 count
133077cb4d3eSLandon J. Fuller			_written = min(_count, UInt8Max)
133177cb4d3eSLandon J. Fuller
133277cb4d3eSLandon J. Fuller			srom_ops_emit_opcode(opstream,
133377cb4d3eSLandon J. Fuller			    sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
133477cb4d3eSLandon J. Fuller				SPROM_OPCODE_DO_BINDN,
133577cb4d3eSLandon J. Fuller				_si_signbit,
133677cb4d3eSLandon J. Fuller				abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
133777cb4d3eSLandon J. Fuller				_skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT),
133877cb4d3eSLandon J. Fuller			    _written)
133977cb4d3eSLandon J. Fuller			_count -= _written
134077cb4d3eSLandon J. Fuller
134177cb4d3eSLandon J. Fuller		} else {
134277cb4d3eSLandon J. Fuller			# The 1-byte BIND form can encode the same SKIP values
134377cb4d3eSLandon J. Fuller			# as the 2-byte BINDN, with a implicit count of 1
134477cb4d3eSLandon J. Fuller			srom_ops_emit_opcode(opstream,
134577cb4d3eSLandon J. Fuller			    sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
134677cb4d3eSLandon J. Fuller				SPROM_OPCODE_DO_BIND,
134777cb4d3eSLandon J. Fuller				_si_signbit,
134877cb4d3eSLandon J. Fuller				abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
134977cb4d3eSLandon J. Fuller				_skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT))
135077cb4d3eSLandon J. Fuller			_count--
135177cb4d3eSLandon J. Fuller		}
135277cb4d3eSLandon J. Fuller	}
135377cb4d3eSLandon J. Fuller
135477cb4d3eSLandon J. Fuller	# Update the stream's input offset
135577cb4d3eSLandon J. Fuller	_srom_ops_apply_bind_offset(opstream, bind)
135677cb4d3eSLandon J. Fuller
135777cb4d3eSLandon J. Fuller	# Write any buffered post-BIND opcodes
135877cb4d3eSLandon J. Fuller	_buffer = get(bind, p_buffer)
135977cb4d3eSLandon J. Fuller	_nbuffer = array_size(_buffer)
136077cb4d3eSLandon J. Fuller	for (_i = 0; _i < _nbuffer; _i++)
136177cb4d3eSLandon J. Fuller		srom_ops_emit(opstream, array_get(_buffer, _i))
136277cb4d3eSLandon J. Fuller}
136377cb4d3eSLandon J. Fuller
136477cb4d3eSLandon J. Fuller# Flush any buffered binding
136577cb4d3eSLandon J. Fullerfunction srom_ops_flush_bind(opstream, allow_implicit, _bind, _bind_total)
136677cb4d3eSLandon J. Fuller{
136777cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
136877cb4d3eSLandon J. Fuller
136977cb4d3eSLandon J. Fuller	# If no pending bind, nothing to flush
137077cb4d3eSLandon J. Fuller	if ((_bind = get(opstream, p_pending_bind)) == null)
137177cb4d3eSLandon J. Fuller		return
137277cb4d3eSLandon J. Fuller
137377cb4d3eSLandon J. Fuller	# Check the per-variable bind count to determine whether
137477cb4d3eSLandon J. Fuller	# we can encode an implicit bind.
137577cb4d3eSLandon J. Fuller	#
137677cb4d3eSLandon J. Fuller	# If there have been any explicit bind statements, implicit binding
137777cb4d3eSLandon J. Fuller	# cannot be used.
137877cb4d3eSLandon J. Fuller	_bind_total = get(opstream, p_bind_total)
137977cb4d3eSLandon J. Fuller	if (allow_implicit && _bind_total > 0) {
138077cb4d3eSLandon J. Fuller		# Disable implicit encoding; explicit bind statements have
138177cb4d3eSLandon J. Fuller		# been issued for this variable previously.
138277cb4d3eSLandon J. Fuller		allow_implicit = 0
138377cb4d3eSLandon J. Fuller	}
138477cb4d3eSLandon J. Fuller
138577cb4d3eSLandon J. Fuller	# Increment bind count
138677cb4d3eSLandon J. Fuller	set(opstream, p_bind_total, _bind_total + 1)
138777cb4d3eSLandon J. Fuller
138877cb4d3eSLandon J. Fuller	# Clear the property value
138977cb4d3eSLandon J. Fuller	set(opstream, p_pending_bind, null)
139077cb4d3eSLandon J. Fuller
139177cb4d3eSLandon J. Fuller	# If a pending bind operation can be encoded as an implicit bind,
139277cb4d3eSLandon J. Fuller	# emit a descriptive comment and update the stream state.
139377cb4d3eSLandon J. Fuller	#
139477cb4d3eSLandon J. Fuller	# Otherwise, emit the full set of bind opcode(s)
139577cb4d3eSLandon J. Fuller	_base_off = get(opstream, p_offset)
139677cb4d3eSLandon J. Fuller	if (allow_implicit && srom_opbind_is_implicit_encodable(_bind)) {
139777cb4d3eSLandon J. Fuller		# Update stream's input offset
139877cb4d3eSLandon J. Fuller		_srom_ops_apply_bind_offset(opstream, _bind)
139977cb4d3eSLandon J. Fuller	} else {
140077cb4d3eSLandon J. Fuller		_srom_ops_emit_bind(opstream, _bind)
140177cb4d3eSLandon J. Fuller	}
140277cb4d3eSLandon J. Fuller
140377cb4d3eSLandon J. Fuller	# Provide bind information as a comment
140477cb4d3eSLandon J. Fuller	srom_ops_emit(opstream,
140577cb4d3eSLandon J. Fuller	    sprintf("/* bind (%s @ %#x -> %#x) */\n",
140677cb4d3eSLandon J. Fuller		type_to_string(get(opstream, p_type)),
140777cb4d3eSLandon J. Fuller		_base_off, get(opstream, p_offset)))
140877cb4d3eSLandon J. Fuller
140977cb4d3eSLandon J. Fuller	# Clean up
141077cb4d3eSLandon J. Fuller	obj_delete(_bind)
141177cb4d3eSLandon J. Fuller}
141277cb4d3eSLandon J. Fuller
141377cb4d3eSLandon J. Fuller# Write OPCODE_EOF after flushing any buffered writes
141477cb4d3eSLandon J. Fullerfunction srom_ops_emit_eof(opstream) {
141577cb4d3eSLandon J. Fuller	obj_assert_class(opstream, SromOpStream)
141677cb4d3eSLandon J. Fuller
141777cb4d3eSLandon J. Fuller	# Flush any buffered writes
141877cb4d3eSLandon J. Fuller	srom_ops_flush_bind(opstream, 1)
141977cb4d3eSLandon J. Fuller
142077cb4d3eSLandon J. Fuller	# Emit an explicit VAR_END opcode for the last entry
142177cb4d3eSLandon J. Fuller	srom_ops_emit_opcode(opstream, SPROM_OPCODE_VAR_END)
142277cb4d3eSLandon J. Fuller
142377cb4d3eSLandon J. Fuller	# Emit EOF
142477cb4d3eSLandon J. Fuller	srom_ops_emit_opcode(opstream, SPROM_OPCODE_EOF)
142577cb4d3eSLandon J. Fuller}
142677cb4d3eSLandon J. Fuller
142777cb4d3eSLandon J. Fuller# Write the SROM offset segment bindings to the opstream
142877cb4d3eSLandon J. Fullerfunction write_srom_offset_bindings(opstream, offsets,
142977cb4d3eSLandon J. Fuller    _noffsets, _offset, _segs, _nsegs, _segment, _cont,
143077cb4d3eSLandon J. Fuller    _i, _j)
143177cb4d3eSLandon J. Fuller{
143277cb4d3eSLandon J. Fuller	_noffsets = array_size(offsets)
143377cb4d3eSLandon J. Fuller	for (_i = 0; _i < _noffsets; _i++) {
143477cb4d3eSLandon J. Fuller		# Encode each segment in this offset
143577cb4d3eSLandon J. Fuller		_offset = array_get(offsets, _i)
143677cb4d3eSLandon J. Fuller		_segs = get(_offset, p_segments)
143777cb4d3eSLandon J. Fuller		_nsegs = array_size(_segs)
143877cb4d3eSLandon J. Fuller
143977cb4d3eSLandon J. Fuller		for (_j = 0; _j < _nsegs; _j++) {
144077cb4d3eSLandon J. Fuller			_segment = array_get(_segs, _j)
144177cb4d3eSLandon J. Fuller			_cont = 0
144277cb4d3eSLandon J. Fuller
144377cb4d3eSLandon J. Fuller			# Should this value be OR'd with the next segment?
144477cb4d3eSLandon J. Fuller			if (_j+1 < _nsegs)
144577cb4d3eSLandon J. Fuller				_cont = 1
144677cb4d3eSLandon J. Fuller
144777cb4d3eSLandon J. Fuller			# Encode segment
144877cb4d3eSLandon J. Fuller			srom_ops_emit_segment(opstream, _segment, _cont)
144977cb4d3eSLandon J. Fuller		}
145077cb4d3eSLandon J. Fuller	}
145177cb4d3eSLandon J. Fuller}
145277cb4d3eSLandon J. Fuller
145377cb4d3eSLandon J. Fuller# Write the SROM entry stream for a SROM entry to the output file
145477cb4d3eSLandon J. Fullerfunction write_srom_entry_bindings(entry, opstream, _var, _vid,
145577cb4d3eSLandon J. Fuller    _var_type, _entry_type, _offsets, _noffsets)
145677cb4d3eSLandon J. Fuller{
145777cb4d3eSLandon J. Fuller	_var = get(entry, p_var)
145877cb4d3eSLandon J. Fuller	_vid = get(_var, p_vid)
145977cb4d3eSLandon J. Fuller
146077cb4d3eSLandon J. Fuller	# Encode revision switch. This resets variable state, so must
146177cb4d3eSLandon J. Fuller	# occur before any variable definitions to which it applies
146277cb4d3eSLandon J. Fuller	srom_ops_emit_revisions(opstream, get(entry, p_revisions))
146377cb4d3eSLandon J. Fuller
146477cb4d3eSLandon J. Fuller	# Encode variable ID
146577cb4d3eSLandon J. Fuller	srom_ops_reset_var(opstream, _var, _vid)
146677cb4d3eSLandon J. Fuller	output_depth++
146777cb4d3eSLandon J. Fuller
146877cb4d3eSLandon J. Fuller	# Write entry-specific array length (SROM layouts may define array
146977cb4d3eSLandon J. Fuller	# mappings with fewer elements than in the variable definition)
147077cb4d3eSLandon J. Fuller	if (srom_entry_has_array_type(entry)) {
147177cb4d3eSLandon J. Fuller		_var_type = get(_var, p_type)
147277cb4d3eSLandon J. Fuller		_entry_type = get(entry, p_type)
147377cb4d3eSLandon J. Fuller
147477cb4d3eSLandon J. Fuller		# If the array length differs from the variable default,
147577cb4d3eSLandon J. Fuller		# write an OPCODE_EXT_NELEM entry
147677cb4d3eSLandon J. Fuller		if (type_get_nelem(_var_type) != type_get_nelem(_entry_type)) {
147777cb4d3eSLandon J. Fuller			srom_ops_emit_opcode(opstream, SPROM_OPCODE_NELEM,
147877cb4d3eSLandon J. Fuller			    srom_entry_get_array_len(entry))
147977cb4d3eSLandon J. Fuller		}
148077cb4d3eSLandon J. Fuller	}
148177cb4d3eSLandon J. Fuller
148277cb4d3eSLandon J. Fuller	# Write offset segment bindings
148377cb4d3eSLandon J. Fuller	_offsets = get(entry, p_offsets)
148477cb4d3eSLandon J. Fuller	write_srom_offset_bindings(opstream, _offsets)
148577cb4d3eSLandon J. Fuller	output_depth--
148677cb4d3eSLandon J. Fuller}
148777cb4d3eSLandon J. Fuller
148877cb4d3eSLandon J. Fuller# Write a SROM layout binding opcode table to the output file
148977cb4d3eSLandon J. Fullerfunction write_srom_bindings(layout, _varname, _var, _all_entries,
149077cb4d3eSLandon J. Fuller    _nall_entries, _entries, _nentries, _entry, _opstream, _i)
149177cb4d3eSLandon J. Fuller{
149277cb4d3eSLandon J. Fuller	_varname = srom_layout_get_variable_name(layout)
149377cb4d3eSLandon J. Fuller	_all_entries = get(layout, p_entries)
149477cb4d3eSLandon J. Fuller	_opstream = srom_ops_new(layout)
149577cb4d3eSLandon J. Fuller
149677cb4d3eSLandon J. Fuller	#
149777cb4d3eSLandon J. Fuller	# Collect all entries to be included in the output, and then
149877cb4d3eSLandon J. Fuller	# sort by their variable's assigned ID (ascending).
149977cb4d3eSLandon J. Fuller	#
150077cb4d3eSLandon J. Fuller	# The variable IDs were previously assigned in lexigraphical sort
150177cb4d3eSLandon J. Fuller	# order; since the variable *offsets* tend to match this order, this
150277cb4d3eSLandon J. Fuller	# works out well for our compact encoding, allowing us to make use of
150377cb4d3eSLandon J. Fuller	# compact relative encoding of both variable IDs and variable offsets.
150477cb4d3eSLandon J. Fuller	#
150577cb4d3eSLandon J. Fuller	_entries = array_new()
150677cb4d3eSLandon J. Fuller	_nall_entries = array_size(_all_entries)
150777cb4d3eSLandon J. Fuller	for (_i = 0; _i < _nall_entries; _i++) {
150877cb4d3eSLandon J. Fuller		_entry = array_get(_all_entries, _i)
150977cb4d3eSLandon J. Fuller		_var = get(_entry, p_var)
151077cb4d3eSLandon J. Fuller
151177cb4d3eSLandon J. Fuller		# Skip internal variables
151277cb4d3eSLandon J. Fuller		if (var_is_internal(_var))
151377cb4d3eSLandon J. Fuller			continue
151477cb4d3eSLandon J. Fuller
151577cb4d3eSLandon J. Fuller		# Sanity check variable ID assignment
151677cb4d3eSLandon J. Fuller		if (get(_var, p_vid) == "")
151777cb4d3eSLandon J. Fuller			errorx("missing variable ID for " obj_to_string(_var))
151877cb4d3eSLandon J. Fuller
151977cb4d3eSLandon J. Fuller		array_append(_entries, _entry)
152077cb4d3eSLandon J. Fuller	}
152177cb4d3eSLandon J. Fuller
1522abc551b1SLandon J. Fuller	# Sort entries by (variable ID, revision range), ascending
1523abc551b1SLandon J. Fuller	array_sort(_entries, prop_path_create(p_var, p_vid),
1524abc551b1SLandon J. Fuller	    prop_path_create(p_revisions, p_start),
1525abc551b1SLandon J. Fuller	    prop_path_create(p_revisions, p_end))
152677cb4d3eSLandon J. Fuller
152777cb4d3eSLandon J. Fuller	# Emit all entry binding opcodes
152877cb4d3eSLandon J. Fuller	emit("static const uint8_t " _varname "[] = {\n")
152977cb4d3eSLandon J. Fuller	output_depth++
153077cb4d3eSLandon J. Fuller
153177cb4d3eSLandon J. Fuller	_nentries = array_size(_entries)
153277cb4d3eSLandon J. Fuller	for (_i = 0; _i < _nentries; _i++) {
153377cb4d3eSLandon J. Fuller		_entry = array_get(_entries, _i)
153477cb4d3eSLandon J. Fuller		write_srom_entry_bindings(_entry, _opstream)
153577cb4d3eSLandon J. Fuller	}
153677cb4d3eSLandon J. Fuller
153777cb4d3eSLandon J. Fuller	# Flush and write EOF
153877cb4d3eSLandon J. Fuller	srom_ops_emit_eof(_opstream)
153977cb4d3eSLandon J. Fuller
154077cb4d3eSLandon J. Fuller	output_depth--
154177cb4d3eSLandon J. Fuller	emit("};\n")
154277cb4d3eSLandon J. Fuller
154377cb4d3eSLandon J. Fuller	obj_delete(_opstream)
154477cb4d3eSLandon J. Fuller	obj_delete(_entries)
154577cb4d3eSLandon J. Fuller}
154677cb4d3eSLandon J. Fuller
154777cb4d3eSLandon J. Fuller# Write the BHND_NVAR_<NAME>_ID #defines to the output file
154877cb4d3eSLandon J. Fullerfunction write_data_defines(output_vars, _noutput_vars, _tab_align, _var,
154977cb4d3eSLandon J. Fuller    _macro, _macros, _num_macros, _i)
155077cb4d3eSLandon J. Fuller{
155177cb4d3eSLandon J. Fuller	# Produce our array of #defines
155277cb4d3eSLandon J. Fuller	_num_macros = 0
155377cb4d3eSLandon J. Fuller	_noutput_vars = array_size(output_vars)
155477cb4d3eSLandon J. Fuller	for (_i = 0; _i < _noutput_vars; _i++) {
155577cb4d3eSLandon J. Fuller		_var = array_get(output_vars, _i)
155677cb4d3eSLandon J. Fuller
155777cb4d3eSLandon J. Fuller		# Variable ID
155877cb4d3eSLandon J. Fuller		_macro = var_get_macro(_var, MTypeVarID, get(_var, p_vid))
155977cb4d3eSLandon J. Fuller		_macros[_num_macros++] = _macro
156077cb4d3eSLandon J. Fuller	}
156177cb4d3eSLandon J. Fuller
156277cb4d3eSLandon J. Fuller	# Calculate value tab alignment position for our macros
156377cb4d3eSLandon J. Fuller	_tab_align = macros_get_tab_alignment(_macros, _num_macros)
156477cb4d3eSLandon J. Fuller
156577cb4d3eSLandon J. Fuller	# Write the #defines
156677cb4d3eSLandon J. Fuller	emit("/* ID constants provide an index into the variable array */\n")
156777cb4d3eSLandon J. Fuller	for (_i = 0; _i < _num_macros; _i++)
156877cb4d3eSLandon J. Fuller		write_macro_define(_macros[_i], _tab_align)
156977cb4d3eSLandon J. Fuller	emit("\n\n");
157077cb4d3eSLandon J. Fuller}
157177cb4d3eSLandon J. Fuller
157277cb4d3eSLandon J. Fuller# Calculate the common tab alignment to be used with a set of prefix strings
157377cb4d3eSLandon J. Fuller# with the given maximum length
157477cb4d3eSLandon J. Fullerfunction tab_alignment(max_len, _tab_align) {
157577cb4d3eSLandon J. Fuller	_tab_align = max_len
157677cb4d3eSLandon J. Fuller	_tab_align += (TAB_WIDTH - (_tab_align % TAB_WIDTH)) % TAB_WIDTH
157777cb4d3eSLandon J. Fuller	_tab_align /= TAB_WIDTH
157877cb4d3eSLandon J. Fuller
157977cb4d3eSLandon J. Fuller	return (_tab_align)
158077cb4d3eSLandon J. Fuller}
158177cb4d3eSLandon J. Fuller
158277cb4d3eSLandon J. Fuller# Generate and return a tab string that can be appended to a string of
158377cb4d3eSLandon J. Fuller# `strlen` to pad the column out to `align_to`
158477cb4d3eSLandon J. Fuller#
158577cb4d3eSLandon J. Fuller# Note: If the string from which strlen was derived contains tabs, the result
158677cb4d3eSLandon J. Fuller# is undefined
158777cb4d3eSLandon J. Fullerfunction tab_str(strlen, align_to, _lead, _pad, _result, _i) {
158877cb4d3eSLandon J. Fuller	_lead = strlen
158977cb4d3eSLandon J. Fuller	_lead -= (_lead % TAB_WIDTH);
159077cb4d3eSLandon J. Fuller	_lead /= TAB_WIDTH;
159177cb4d3eSLandon J. Fuller
159277cb4d3eSLandon J. Fuller	# Determine required padding to reach the desired alignment
159377cb4d3eSLandon J. Fuller	if (align_to >= _lead)
159477cb4d3eSLandon J. Fuller		_pad = align_to - _lead;
159577cb4d3eSLandon J. Fuller	else
159677cb4d3eSLandon J. Fuller		_pad = 1;
159777cb4d3eSLandon J. Fuller
159877cb4d3eSLandon J. Fuller	for (_i = 0; _i < _pad; _i++)
159977cb4d3eSLandon J. Fuller		_result = _result "\t"
1600e83ce340SAdrian Chadd
1601e83ce340SAdrian Chadd	return (_result)
1602e83ce340SAdrian Chadd}
1603e83ce340SAdrian Chadd
1604e83ce340SAdrian Chadd
160577cb4d3eSLandon J. Fuller# Write a MacroDefine constant, padding the constant out to `align_to`
160677cb4d3eSLandon J. Fullerfunction write_macro_define(macro, align_to, _tabstr, _i) {
160777cb4d3eSLandon J. Fuller	# Determine required padding to reach the desired alignment
160877cb4d3eSLandon J. Fuller	_tabstr = tab_str(length(get(macro, p_name)), align_to)
1609e83ce340SAdrian Chadd
161077cb4d3eSLandon J. Fuller	emit("#define\t" get(macro, p_name) _tabstr get(macro, p_value) "\n")
1611e83ce340SAdrian Chadd}
1612e83ce340SAdrian Chadd
161377cb4d3eSLandon J. Fuller# Calculate the tab alignment to be used with a given integer-indexed array
161477cb4d3eSLandon J. Fuller# of Macro instances.
161577cb4d3eSLandon J. Fullerfunction macros_get_tab_alignment(macros, macros_len, _macro, _max_len, _i) {
161677cb4d3eSLandon J. Fuller	_max_len = 0
161777cb4d3eSLandon J. Fuller	for (_i = 0; _i < macros_len; _i++) {
161877cb4d3eSLandon J. Fuller		_macro = macros[_i]
161977cb4d3eSLandon J. Fuller		_max_len = max(_max_len, length(get(_macro, p_name)))
162077cb4d3eSLandon J. Fuller	}
162177cb4d3eSLandon J. Fuller
162277cb4d3eSLandon J. Fuller	return (tab_alignment(_max_len))
162377cb4d3eSLandon J. Fuller}
162477cb4d3eSLandon J. Fuller
162577cb4d3eSLandon J. Fuller# Variable group block
162677cb4d3eSLandon J. Fuller$1 == "group" && in_parser_context(NVRAM) {
162777cb4d3eSLandon J. Fuller	parse_variable_group()
162877cb4d3eSLandon J. Fuller}
162977cb4d3eSLandon J. Fuller
163077cb4d3eSLandon J. Fuller# Variable definition
163177cb4d3eSLandon J. Fuller(($1 ~ VACCESS_REGEX && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) &&
163277cb4d3eSLandon J. Fuller    in_parser_context(SymbolContext) \
163377cb4d3eSLandon J. Fuller{
163477cb4d3eSLandon J. Fuller	parse_variable_defn()
163577cb4d3eSLandon J. Fuller}
163677cb4d3eSLandon J. Fuller
163777cb4d3eSLandon J. Fuller# Variable "fmt" parameter
163877cb4d3eSLandon J. Fuller$1 == "fmt" && in_parser_context(Var) {
163977cb4d3eSLandon J. Fuller	parse_variable_param($1)
164077cb4d3eSLandon J. Fuller	next
164177cb4d3eSLandon J. Fuller}
164277cb4d3eSLandon J. Fuller
164377cb4d3eSLandon J. Fuller# Variable "all1" parameter
164477cb4d3eSLandon J. Fuller$1 == "all1" && in_parser_context(Var) {
164577cb4d3eSLandon J. Fuller	parse_variable_param($1)
164677cb4d3eSLandon J. Fuller	next
164777cb4d3eSLandon J. Fuller}
164877cb4d3eSLandon J. Fuller
164977cb4d3eSLandon J. Fuller# Variable desc/help parameters
165077cb4d3eSLandon J. Fuller($1 == "desc" || $1 == "help") && in_parser_context(Var) {
165177cb4d3eSLandon J. Fuller	parse_variable_param($1)
165277cb4d3eSLandon J. Fuller	next
165377cb4d3eSLandon J. Fuller}
165477cb4d3eSLandon J. Fuller
165577cb4d3eSLandon J. Fuller# SROM layout block
165677cb4d3eSLandon J. Fuller$1 == "srom" && in_parser_context(NVRAM) {
165777cb4d3eSLandon J. Fuller	parse_srom_layout()
165877cb4d3eSLandon J. Fuller}
165977cb4d3eSLandon J. Fuller
166077cb4d3eSLandon J. Fuller
166177cb4d3eSLandon J. Fuller# SROM layout revision filter block
166277cb4d3eSLandon J. Fuller$1 == "srom" && in_parser_context(SromLayout) {
166377cb4d3eSLandon J. Fuller	parse_srom_layout_filter()
166477cb4d3eSLandon J. Fuller}
166577cb4d3eSLandon J. Fuller
166677cb4d3eSLandon J. Fuller# SROM layout variable entry
166777cb4d3eSLandon J. Fuller$1 ~ "("OFF_REGEX"):$" && \
166877cb4d3eSLandon J. Fuller    (in_parser_context(SromLayout) || in_parser_context(SromLayoutFilter)) \
166977cb4d3eSLandon J. Fuller{
167077cb4d3eSLandon J. Fuller	parse_srom_variable_entry()
167177cb4d3eSLandon J. Fuller}
167277cb4d3eSLandon J. Fuller
167377cb4d3eSLandon J. Fuller
167477cb4d3eSLandon J. Fuller# SROM entry segment
167577cb4d3eSLandon J. Fuller$1 ~ "("REL_OFF_REGEX"|"OFF_REGEX")[:,|]?" && in_parser_context(SromEntry) {
167677cb4d3eSLandon J. Fuller	parse_srom_entry_segments()
167777cb4d3eSLandon J. Fuller}
167877cb4d3eSLandon J. Fuller
167977cb4d3eSLandon J. Fuller# Skip comments and blank lines
168077cb4d3eSLandon J. Fuller/^[ \t]*#/ || /^$/ {
168177cb4d3eSLandon J. Fuller	next
168277cb4d3eSLandon J. Fuller}
168377cb4d3eSLandon J. Fuller
168477cb4d3eSLandon J. Fuller# Close blocks
168577cb4d3eSLandon J. Fuller/}/ && !in_parser_context(NVRAM) {
168677cb4d3eSLandon J. Fuller	while (!in_parser_context(NVRAM) && $0 ~ "}") {
168777cb4d3eSLandon J. Fuller		parser_state_close_block();
168877cb4d3eSLandon J. Fuller	}
168977cb4d3eSLandon J. Fuller	next
169077cb4d3eSLandon J. Fuller}
169177cb4d3eSLandon J. Fuller
169277cb4d3eSLandon J. Fuller# Report unbalanced '}'
169377cb4d3eSLandon J. Fuller/}/ && in_parser_context(NVRAM) {
169477cb4d3eSLandon J. Fuller	error("extra '}'")
169577cb4d3eSLandon J. Fuller}
169677cb4d3eSLandon J. Fuller
169777cb4d3eSLandon J. Fuller# Invalid variable type
169877cb4d3eSLandon J. Fuller$1 && in_parser_context(SymbolContext) {
169977cb4d3eSLandon J. Fuller	error("unknown type '" $1 "'")
170077cb4d3eSLandon J. Fuller}
170177cb4d3eSLandon J. Fuller
170277cb4d3eSLandon J. Fuller# Generic parse failure
170377cb4d3eSLandon J. Fuller{
170477cb4d3eSLandon J. Fuller	error("unrecognized statement")
170577cb4d3eSLandon J. Fuller}
170677cb4d3eSLandon J. Fuller
170777cb4d3eSLandon J. Fuller# Create a class instance with the given name
170877cb4d3eSLandon J. Fullerfunction class_new(name, superclass, _class) {
170977cb4d3eSLandon J. Fuller	if (_class != null)
171077cb4d3eSLandon J. Fuller		errorx("class_get() must be called with one or two arguments")
171177cb4d3eSLandon J. Fuller
171277cb4d3eSLandon J. Fuller	# Look for an existing class instance
171377cb4d3eSLandon J. Fuller	if (name in _g_class_names)
171477cb4d3eSLandon J. Fuller		errorx("redefining class: " name)
171577cb4d3eSLandon J. Fuller
171677cb4d3eSLandon J. Fuller	# Create and register the class object
171777cb4d3eSLandon J. Fuller	_class = obj_new(superclass)
171877cb4d3eSLandon J. Fuller	_g_class_names[name] = _class
171977cb4d3eSLandon J. Fuller	_g_obj[_class,OBJ_IS_CLS] = 1
172077cb4d3eSLandon J. Fuller	_g_obj[_class,CLS_NAME] = name
172177cb4d3eSLandon J. Fuller
172277cb4d3eSLandon J. Fuller	return (_class)
172377cb4d3eSLandon J. Fuller}
172477cb4d3eSLandon J. Fuller
172577cb4d3eSLandon J. Fuller# Return the class instance with the given name
172677cb4d3eSLandon J. Fullerfunction class_get(name) {
172777cb4d3eSLandon J. Fuller	if (name in _g_class_names)
172877cb4d3eSLandon J. Fuller		return (_g_class_names[name])
172977cb4d3eSLandon J. Fuller
173077cb4d3eSLandon J. Fuller	errorx("no such class " name)
173177cb4d3eSLandon J. Fuller}
173277cb4d3eSLandon J. Fuller
173377cb4d3eSLandon J. Fuller# Return the name of cls
173477cb4d3eSLandon J. Fullerfunction class_get_name(cls) {
173577cb4d3eSLandon J. Fuller	if (cls == null) {
173677cb4d3eSLandon J. Fuller		warnx("class_get_name() called with null class")
173777cb4d3eSLandon J. Fuller		return "<null>"
173877cb4d3eSLandon J. Fuller	}
173977cb4d3eSLandon J. Fuller
174077cb4d3eSLandon J. Fuller	if (!obj_is_class(cls))
174177cb4d3eSLandon J. Fuller		errorx(cls " is not a class object")
174277cb4d3eSLandon J. Fuller
174377cb4d3eSLandon J. Fuller	return (_g_obj[cls,CLS_NAME])
174477cb4d3eSLandon J. Fuller}
174577cb4d3eSLandon J. Fuller
174677cb4d3eSLandon J. Fuller# Return true if the given property property ID is defined on class
174777cb4d3eSLandon J. Fullerfunction class_has_prop_id(class, prop_id, _super) {
174877cb4d3eSLandon J. Fuller	if (_super != null)
174977cb4d3eSLandon J. Fuller		errorx("class_has_prop_id() must be called with two arguments")
175077cb4d3eSLandon J. Fuller
175177cb4d3eSLandon J. Fuller	if (class == null)
175277cb4d3eSLandon J. Fuller		return (0)
175377cb4d3eSLandon J. Fuller
1754c283839dSLandon J. Fuller	if (prop_id == null)
1755c283839dSLandon J. Fuller		return (0)
1756c283839dSLandon J. Fuller
175777cb4d3eSLandon J. Fuller	# Check class<->prop cache
175877cb4d3eSLandon J. Fuller	if ((class, prop_id) in _g_class_prop_cache)
175977cb4d3eSLandon J. Fuller		return (1)
176077cb4d3eSLandon J. Fuller
176177cb4d3eSLandon J. Fuller	# Otherwise, slow path
176277cb4d3eSLandon J. Fuller	if (!obj_is_class(class))
176377cb4d3eSLandon J. Fuller		errorx(class " is not a class object")
176477cb4d3eSLandon J. Fuller
176577cb4d3eSLandon J. Fuller	if (_super != null)
176677cb4d3eSLandon J. Fuller		errorx("class_has_prop_id() must be called with two arguments")
176777cb4d3eSLandon J. Fuller
176877cb4d3eSLandon J. Fuller	for (_super = class; _super != null; _super = obj_get_class(_super)) {
176977cb4d3eSLandon J. Fuller		if (!((_super,CLS_PROP,prop_id) in _g_obj))
177077cb4d3eSLandon J. Fuller			continue
177177cb4d3eSLandon J. Fuller
177277cb4d3eSLandon J. Fuller		# Found; add to class<->prop cache
177377cb4d3eSLandon J. Fuller		_g_class_prop_cache[class,prop_id] = 1
177477cb4d3eSLandon J. Fuller		return (1)
177577cb4d3eSLandon J. Fuller	}
177677cb4d3eSLandon J. Fuller
177777cb4d3eSLandon J. Fuller	return (0)
177877cb4d3eSLandon J. Fuller}
177977cb4d3eSLandon J. Fuller
178077cb4d3eSLandon J. Fuller# Return true if the given property prop is defined on class
178177cb4d3eSLandon J. Fullerfunction class_has_property(class, prop) {
178277cb4d3eSLandon J. Fuller	if (!(PROP_ID in prop))
178377cb4d3eSLandon J. Fuller		return (0)
178477cb4d3eSLandon J. Fuller
178577cb4d3eSLandon J. Fuller	return (class_has_prop_id(class, prop[PROP_ID]))
178677cb4d3eSLandon J. Fuller}
178777cb4d3eSLandon J. Fuller
178877cb4d3eSLandon J. Fuller# Define a `prop` on `class` with the given `name` string
178977cb4d3eSLandon J. Fullerfunction class_add_prop(class, prop, name, _prop_id) {
179077cb4d3eSLandon J. Fuller	if (_prop_id != null)
179177cb4d3eSLandon J. Fuller		errorx("class_add_prop() must be called with three arguments")
179277cb4d3eSLandon J. Fuller
179377cb4d3eSLandon J. Fuller	# Check for duplicate property definition
179477cb4d3eSLandon J. Fuller	if (class_has_property(class, prop))
179577cb4d3eSLandon J. Fuller		errorx("property " prop[PROP_NAME] " already defined on " \
179677cb4d3eSLandon J. Fuller		    class_get_name(class))
179777cb4d3eSLandon J. Fuller
179877cb4d3eSLandon J. Fuller	# Init property IDs
179977cb4d3eSLandon J. Fuller	if (_g_prop_ids == null)
180077cb4d3eSLandon J. Fuller		_g_prop_ids = 1
180177cb4d3eSLandon J. Fuller
180277cb4d3eSLandon J. Fuller	# Get (or create) new property entry
180377cb4d3eSLandon J. Fuller	if (name in _g_prop_names) {
180477cb4d3eSLandon J. Fuller		_prop_id = _g_prop_names[name]
180577cb4d3eSLandon J. Fuller	} else {
180677cb4d3eSLandon J. Fuller		_prop_id = _g_prop_ids++
180777cb4d3eSLandon J. Fuller		_g_prop_names[name] = _prop_id
180877cb4d3eSLandon J. Fuller		_g_props[_prop_id] = name
180977cb4d3eSLandon J. Fuller
181077cb4d3eSLandon J. Fuller		prop[PROP_NAME]	= name
181177cb4d3eSLandon J. Fuller		prop[PROP_ID]	= _prop_id
181277cb4d3eSLandon J. Fuller	}
181377cb4d3eSLandon J. Fuller
181477cb4d3eSLandon J. Fuller	# Add to class definition
181577cb4d3eSLandon J. Fuller	_g_obj[class,CLS_PROP,prop[PROP_ID]] = name
181677cb4d3eSLandon J. Fuller	return (name)
181777cb4d3eSLandon J. Fuller}
181877cb4d3eSLandon J. Fuller
181977cb4d3eSLandon J. Fuller# Return the property ID for a given class-defined property
182077cb4d3eSLandon J. Fullerfunction class_get_prop_id(class, prop) {
182177cb4d3eSLandon J. Fuller	if (class == null)
182277cb4d3eSLandon J. Fuller		errorx("class_get_prop_id() on null class")
182377cb4d3eSLandon J. Fuller
182477cb4d3eSLandon J. Fuller	if (!class_has_property(class, prop)) {
182577cb4d3eSLandon J. Fuller		errorx("requested undefined property '" prop[PROP_NAME] "on " \
182677cb4d3eSLandon J. Fuller		    class_get_name(class))
182777cb4d3eSLandon J. Fuller	}
182877cb4d3eSLandon J. Fuller
182977cb4d3eSLandon J. Fuller	return (prop[PROP_ID])
183077cb4d3eSLandon J. Fuller}
183177cb4d3eSLandon J. Fuller
183277cb4d3eSLandon J. Fuller# Return the property ID for a given class-defined property name
183377cb4d3eSLandon J. Fullerfunction class_get_named_prop_id(class, name, _prop_id) {
183477cb4d3eSLandon J. Fuller	if (class == null)
183577cb4d3eSLandon J. Fuller		errorx("class_get_prop_id() on null class")
183677cb4d3eSLandon J. Fuller
183777cb4d3eSLandon J. Fuller	if (!(name in _g_prop_names))
183877cb4d3eSLandon J. Fuller		errorx("requested undefined property '" name "'")
183977cb4d3eSLandon J. Fuller
184077cb4d3eSLandon J. Fuller	_prop_id = _g_prop_names[name]
184177cb4d3eSLandon J. Fuller
184277cb4d3eSLandon J. Fuller	if (!class_has_prop_id(class, _prop_id)) {
184377cb4d3eSLandon J. Fuller		errorx("requested undefined property '" _g_props[_prop_id] \
184477cb4d3eSLandon J. Fuller		    "' on " class_get_name(class))
184577cb4d3eSLandon J. Fuller	}
184677cb4d3eSLandon J. Fuller
184777cb4d3eSLandon J. Fuller	return (_prop_id)
184877cb4d3eSLandon J. Fuller}
184977cb4d3eSLandon J. Fuller
185077cb4d3eSLandon J. Fuller# Create a new instance of the given class
185177cb4d3eSLandon J. Fullerfunction obj_new(class, _obj) {
185277cb4d3eSLandon J. Fuller	if (_obj != null)
185377cb4d3eSLandon J. Fuller		errorx("obj_new() must be called with one argument")
185477cb4d3eSLandon J. Fuller
185577cb4d3eSLandon J. Fuller	if (_g_obj_ids == null)
185677cb4d3eSLandon J. Fuller		_g_obj_ids = 1
185777cb4d3eSLandon J. Fuller
185877cb4d3eSLandon J. Fuller	# Assign ID and set superclass
185977cb4d3eSLandon J. Fuller	_obj = _g_obj_ids++
186077cb4d3eSLandon J. Fuller	_g_obj[_obj,OBJ_SUPER] = class
186177cb4d3eSLandon J. Fuller
186277cb4d3eSLandon J. Fuller	return (_obj)
186377cb4d3eSLandon J. Fuller}
186477cb4d3eSLandon J. Fuller
186577cb4d3eSLandon J. Fuller# obj_delete() support for Map instances
186677cb4d3eSLandon J. Fullerfunction _obj_delete_map(obj, _prefix, _key) {
186777cb4d3eSLandon J. Fuller	obj_assert_class(obj, Map)
186877cb4d3eSLandon J. Fuller	_prefix = "^" obj SUBSEP
186977cb4d3eSLandon J. Fuller	for (_key in _g_maps) {
187077cb4d3eSLandon J. Fuller		if (!match(_key, _prefix) && _key != obj)
187177cb4d3eSLandon J. Fuller			continue
187277cb4d3eSLandon J. Fuller		delete _g_maps[_key]
187377cb4d3eSLandon J. Fuller	}
187477cb4d3eSLandon J. Fuller}
187577cb4d3eSLandon J. Fuller
187677cb4d3eSLandon J. Fuller# obj_delete() support for Array instances
187777cb4d3eSLandon J. Fullerfunction _obj_delete_array(obj, _size, _i) {
187877cb4d3eSLandon J. Fuller	obj_assert_class(obj, Array)
187977cb4d3eSLandon J. Fuller	_size = array_size(obj)
188077cb4d3eSLandon J. Fuller
188177cb4d3eSLandon J. Fuller	for (_i = 0; _i < _size; _i++)
188277cb4d3eSLandon J. Fuller		delete _g_arrays[obj,OBJ_PROP,_i]
188377cb4d3eSLandon J. Fuller}
188477cb4d3eSLandon J. Fuller
188577cb4d3eSLandon J. Fuller# Destroy all metadata associated with the given object
188677cb4d3eSLandon J. Fullerfunction obj_delete(obj, _prop_id, _prop_name, _prefix, _key, _size, _i) {
188777cb4d3eSLandon J. Fuller	if (obj_is_class(obj))
188877cb4d3eSLandon J. Fuller		errorx("cannot delete class objects")
188977cb4d3eSLandon J. Fuller
189077cb4d3eSLandon J. Fuller	# Handle classes that use external global array storage
189177cb4d3eSLandon J. Fuller	# for effeciency
189277cb4d3eSLandon J. Fuller	if (obj_is_instanceof(obj, Map)) {
189377cb4d3eSLandon J. Fuller		_obj_delete_map(obj)
189477cb4d3eSLandon J. Fuller	} else if (obj_is_instanceof(obj, Array)) {
189577cb4d3eSLandon J. Fuller		_obj_delete_array(obj)
189677cb4d3eSLandon J. Fuller	}
189777cb4d3eSLandon J. Fuller
189877cb4d3eSLandon J. Fuller	# Delete all object properties
189977cb4d3eSLandon J. Fuller	for (_prop_name in _g_prop_names) {
190077cb4d3eSLandon J. Fuller		if (!obj_has_prop_id(obj, _prop_id))
190177cb4d3eSLandon J. Fuller			continue
190277cb4d3eSLandon J. Fuller
190377cb4d3eSLandon J. Fuller		_prop_id = _g_prop_names[_prop_name]
190477cb4d3eSLandon J. Fuller		delete _g_obj[obj,OBJ_PROP,_prop_id]
190577cb4d3eSLandon J. Fuller		delete _g_obj_nr[obj,OBJ_PROP,_prop_id]
190677cb4d3eSLandon J. Fuller	}
190777cb4d3eSLandon J. Fuller
190877cb4d3eSLandon J. Fuller	# Delete instance state
190977cb4d3eSLandon J. Fuller	delete _g_obj[obj,OBJ_IS_CLS]
191077cb4d3eSLandon J. Fuller	delete _g_obj[obj,OBJ_SUPER]
191177cb4d3eSLandon J. Fuller}
191277cb4d3eSLandon J. Fuller
191377cb4d3eSLandon J. Fuller# Print an object's unique ID, class, and properties to
191477cb4d3eSLandon J. Fuller# stdout
191577cb4d3eSLandon J. Fullerfunction obj_dump(obj, _pname, _prop_id, _prop_val) {
191677cb4d3eSLandon J. Fuller	print(class_get_name(obj_get_class(obj)) "<" obj ">:")
191777cb4d3eSLandon J. Fuller
191877cb4d3eSLandon J. Fuller	# Dump all properties
191977cb4d3eSLandon J. Fuller	for (_pname in _g_prop_names) {
192077cb4d3eSLandon J. Fuller		_prop_id = _g_prop_names[_pname]
192177cb4d3eSLandon J. Fuller
192277cb4d3eSLandon J. Fuller		if (!obj_has_prop_id(obj, _prop_id))
192377cb4d3eSLandon J. Fuller			continue
192477cb4d3eSLandon J. Fuller
192577cb4d3eSLandon J. Fuller		_prop_val = prop_get(obj, _prop_id)
192677cb4d3eSLandon J. Fuller		printf("\t%s: %s\n", _pname, _prop_val)
192777cb4d3eSLandon J. Fuller	}
192877cb4d3eSLandon J. Fuller}
192977cb4d3eSLandon J. Fuller
193077cb4d3eSLandon J. Fuller# Return true if obj is a class object
193177cb4d3eSLandon J. Fullerfunction obj_is_class(obj) {
193277cb4d3eSLandon J. Fuller	return (_g_obj[obj,OBJ_IS_CLS] == 1)
193377cb4d3eSLandon J. Fuller}
193477cb4d3eSLandon J. Fuller
193577cb4d3eSLandon J. Fuller# Return the class of obj, if any.
193677cb4d3eSLandon J. Fullerfunction obj_get_class(obj) {
193777cb4d3eSLandon J. Fuller	if (obj == null)
193877cb4d3eSLandon J. Fuller		errorx("obj_get_class() on null object")
193977cb4d3eSLandon J. Fuller	return (_g_obj[obj,OBJ_SUPER])
194077cb4d3eSLandon J. Fuller}
194177cb4d3eSLandon J. Fuller
194277cb4d3eSLandon J. Fuller# Return true if obj is an instance of the given class
194377cb4d3eSLandon J. Fullerfunction obj_is_instanceof(obj, class, _super) {
194477cb4d3eSLandon J. Fuller	if (_super != null)
194577cb4d3eSLandon J. Fuller		errorx("obj_is_instanceof() must be called with two arguments")
194677cb4d3eSLandon J. Fuller
194777cb4d3eSLandon J. Fuller	if (!obj_is_class(class))
194877cb4d3eSLandon J. Fuller		errorx(class " is not a class object")
194977cb4d3eSLandon J. Fuller
195077cb4d3eSLandon J. Fuller	if (obj == null) {
195177cb4d3eSLandon J. Fuller		errorx("obj_is_instanceof() called with null obj (class " \
195277cb4d3eSLandon J. Fuller		    class_get_name(class) ")")
195377cb4d3eSLandon J. Fuller	}
195477cb4d3eSLandon J. Fuller
195577cb4d3eSLandon J. Fuller	for (_super = obj_get_class(obj); _super != null;
195677cb4d3eSLandon J. Fuller	     _super = obj_get_class(_super))
195777cb4d3eSLandon J. Fuller	{
195877cb4d3eSLandon J. Fuller		if (_super == class)
195977cb4d3eSLandon J. Fuller			return (1)
196077cb4d3eSLandon J. Fuller	}
196177cb4d3eSLandon J. Fuller
196277cb4d3eSLandon J. Fuller	return (0)
196377cb4d3eSLandon J. Fuller}
196477cb4d3eSLandon J. Fuller
196577cb4d3eSLandon J. Fuller# Default object shallow equality implementation. Returns true if the two
196677cb4d3eSLandon J. Fuller# objects share a common superclass and have identity equality across all defined
196777cb4d3eSLandon J. Fuller# properties.
196877cb4d3eSLandon J. Fullerfunction obj_trivially_equal(lhs, rhs, _class, _pname, _prop_id) {
196977cb4d3eSLandon J. Fuller	# Simple case
197077cb4d3eSLandon J. Fuller	if (lhs == rhs)
197177cb4d3eSLandon J. Fuller		return (1)
197277cb4d3eSLandon J. Fuller
197377cb4d3eSLandon J. Fuller	# Must share a common superclass
197477cb4d3eSLandon J. Fuller	_class = obj_get_class(lhs)
197577cb4d3eSLandon J. Fuller	if (_class != obj_get_class(rhs))
197677cb4d3eSLandon J. Fuller		return (0)
197777cb4d3eSLandon J. Fuller
197877cb4d3eSLandon J. Fuller	# Compare all properties
197977cb4d3eSLandon J. Fuller	_prop_count = 0
198077cb4d3eSLandon J. Fuller	for (_pname in _g_prop_names) {
198177cb4d3eSLandon J. Fuller		_prop_id = _g_prop_names[_pname]
198277cb4d3eSLandon J. Fuller
198377cb4d3eSLandon J. Fuller		if (!class_has_prop_id(_class, _prop_id))
198477cb4d3eSLandon J. Fuller			continue
198577cb4d3eSLandon J. Fuller
198677cb4d3eSLandon J. Fuller		if (prop_get(lhs, _prop_id) != prop_get(rhs, _prop_id))
198777cb4d3eSLandon J. Fuller			return (0)
198877cb4d3eSLandon J. Fuller	}
198977cb4d3eSLandon J. Fuller
199077cb4d3eSLandon J. Fuller	# All properties are trivially equal
199177cb4d3eSLandon J. Fuller	return (1)
199277cb4d3eSLandon J. Fuller}
199377cb4d3eSLandon J. Fuller
199477cb4d3eSLandon J. Fuller
199577cb4d3eSLandon J. Fuller# Return a debug string representation of an object's unique ID, class, and
199677cb4d3eSLandon J. Fuller# properties
199777cb4d3eSLandon J. Fullerfunction obj_to_string(obj, _pname, _prop_id, _prop_val, _prop_count, _result) {
199877cb4d3eSLandon J. Fuller	_result = class_get_name(obj_get_class(obj)) "<" obj ">: { "
199977cb4d3eSLandon J. Fuller
200077cb4d3eSLandon J. Fuller	# Fetch all properties
200177cb4d3eSLandon J. Fuller	_prop_count = 0
200277cb4d3eSLandon J. Fuller	for (_pname in _g_prop_names) {
200377cb4d3eSLandon J. Fuller		_prop_id = _g_prop_names[_pname]
200477cb4d3eSLandon J. Fuller
200577cb4d3eSLandon J. Fuller		if (!obj_has_prop_id(obj, _prop_id))
200677cb4d3eSLandon J. Fuller			continue
200777cb4d3eSLandon J. Fuller
200877cb4d3eSLandon J. Fuller		if (_prop_count >= 0)
200977cb4d3eSLandon J. Fuller			_result = _result ", "
201077cb4d3eSLandon J. Fuller
201177cb4d3eSLandon J. Fuller		_result = _result sprintf("\t%s: %s\n", _pname, _prop_val)
201277cb4d3eSLandon J. Fuller		_prop_count++
201377cb4d3eSLandon J. Fuller	}
201477cb4d3eSLandon J. Fuller
201577cb4d3eSLandon J. Fuller	return (_result " }")
201677cb4d3eSLandon J. Fuller}
201777cb4d3eSLandon J. Fuller
201877cb4d3eSLandon J. Fuller# Assert that obj is an instance of the given class
201977cb4d3eSLandon J. Fullerfunction obj_assert_class(obj, class) {
202077cb4d3eSLandon J. Fuller	if (!obj_is_instanceof(obj, class)) {
202177cb4d3eSLandon J. Fuller		errorx(class_get_name(obj_get_class(obj)) "<" obj "> is not " \
202277cb4d3eSLandon J. Fuller		    "an instance of " class_get_name(class))
202377cb4d3eSLandon J. Fuller	}
202477cb4d3eSLandon J. Fuller}
202577cb4d3eSLandon J. Fuller
202677cb4d3eSLandon J. Fuller# Return true if the given property prop is defined by the object's superclass
202777cb4d3eSLandon J. Fullerfunction obj_has_property(obj, prop, _class) {
202877cb4d3eSLandon J. Fuller	if (obj == null)
202977cb4d3eSLandon J. Fuller		errorx("obj_has_property() on null object")
203077cb4d3eSLandon J. Fuller
203177cb4d3eSLandon J. Fuller	_class = obj_get_class(obj)
203277cb4d3eSLandon J. Fuller	return (class_has_property(_class, prop))
203377cb4d3eSLandon J. Fuller}
203477cb4d3eSLandon J. Fuller
203577cb4d3eSLandon J. Fuller# Return true if the given property ID is defined by the object's superclass
203677cb4d3eSLandon J. Fullerfunction obj_has_prop_id(obj, prop_id, _class) {
203777cb4d3eSLandon J. Fuller	if (obj == null)
203877cb4d3eSLandon J. Fuller		errorx("obj_has_prop_id() on null object")
203977cb4d3eSLandon J. Fuller
204077cb4d3eSLandon J. Fuller	_class = obj_get_class(obj)
204177cb4d3eSLandon J. Fuller	return (class_has_prop_id(_class, prop_id))
204277cb4d3eSLandon J. Fuller}
204377cb4d3eSLandon J. Fuller
204477cb4d3eSLandon J. Fuller# Return the line (NR) at which a given property ID was set on the object
204577cb4d3eSLandon J. Fuller# Will throw an error if the property has not been set on obj
204677cb4d3eSLandon J. Fullerfunction obj_get_prop_id_nr(obj, prop_id) {
204777cb4d3eSLandon J. Fuller	if (obj == null)
204877cb4d3eSLandon J. Fuller		errorx("obj_get_prop_id_nr() on null object")
204977cb4d3eSLandon J. Fuller
205077cb4d3eSLandon J. Fuller	if (!obj_has_prop_id(obj, prop_id)) {
205177cb4d3eSLandon J. Fuller		errorx("requested undefined property '" _g_props[prop_id] \
205277cb4d3eSLandon J. Fuller		    "' (" prop_id ") on " obj_to_string(obj))
205377cb4d3eSLandon J. Fuller	}
205477cb4d3eSLandon J. Fuller
205577cb4d3eSLandon J. Fuller	# Fetch NR
205677cb4d3eSLandon J. Fuller	if ((obj,OBJ_PROP,prop_id) in _g_obj_nr)
205777cb4d3eSLandon J. Fuller		return (_g_obj_nr[obj,OBJ_PROP,prop_id])
205877cb4d3eSLandon J. Fuller
205977cb4d3eSLandon J. Fuller	errorx("property '" _g_props[prop_id] "' (" prop_id ") not " \
206077cb4d3eSLandon J. Fuller	    "previously set on " obj_to_string(obj))
206177cb4d3eSLandon J. Fuller}
206277cb4d3eSLandon J. Fuller
206377cb4d3eSLandon J. Fuller# Return the line (NR) at which a given property was set on the object
206477cb4d3eSLandon J. Fuller# Will throw an error if the property has not been set on obj
206577cb4d3eSLandon J. Fullerfunction obj_get_prop_nr(obj, prop) {
206677cb4d3eSLandon J. Fuller	return (obj_get_prop_id_nr(obj, prop[PROP_ID]))
206777cb4d3eSLandon J. Fuller}
206877cb4d3eSLandon J. Fuller
206977cb4d3eSLandon J. Fuller# Return an abstract property ID for a given property
207077cb4d3eSLandon J. Fullerfunction obj_get_prop_id(obj, prop) {
207177cb4d3eSLandon J. Fuller	if (obj == null)
207277cb4d3eSLandon J. Fuller		errorx("obj_get_prop_id() on null object")
207377cb4d3eSLandon J. Fuller
207477cb4d3eSLandon J. Fuller	return (class_get_prop_id(obj_get_class(obj), prop))
207577cb4d3eSLandon J. Fuller}
207677cb4d3eSLandon J. Fuller
207777cb4d3eSLandon J. Fuller
207877cb4d3eSLandon J. Fuller# Return the property ID for a given property name
207977cb4d3eSLandon J. Fullerfunction obj_get_named_prop_id(obj, name) {
208077cb4d3eSLandon J. Fuller	if (obj == null)
208177cb4d3eSLandon J. Fuller		errorx("obj_get_named_prop_id() on null object")
208277cb4d3eSLandon J. Fuller
208377cb4d3eSLandon J. Fuller	return (class_get_named_prop_id(obj_get_class(obj), name))
208477cb4d3eSLandon J. Fuller}
208577cb4d3eSLandon J. Fuller
208677cb4d3eSLandon J. Fuller# Set a property on obj
208777cb4d3eSLandon J. Fullerfunction set(obj, prop, value, _class) {
208877cb4d3eSLandon J. Fuller	return (prop_set(obj, prop[PROP_ID], value))
208977cb4d3eSLandon J. Fuller}
209077cb4d3eSLandon J. Fuller
209177cb4d3eSLandon J. Fuller# Get a property value defined on obj
209277cb4d3eSLandon J. Fullerfunction get(obj, prop, _class) {
209377cb4d3eSLandon J. Fuller	return (prop_get(obj, prop[PROP_ID]))
209477cb4d3eSLandon J. Fuller}
209577cb4d3eSLandon J. Fuller
209677cb4d3eSLandon J. Fuller# Set a property on obj, using a property ID returned by obj_get_prop_id() or
209777cb4d3eSLandon J. Fuller# class_get_prop_id()
209877cb4d3eSLandon J. Fullerfunction prop_set(obj, prop_id, value, _class) {
209977cb4d3eSLandon J. Fuller	if (obj == null) {
210077cb4d3eSLandon J. Fuller		errorx("setting property '" _g_props[prop_id] \
210177cb4d3eSLandon J. Fuller		    "' on null object")
210277cb4d3eSLandon J. Fuller	}
210377cb4d3eSLandon J. Fuller
210477cb4d3eSLandon J. Fuller	_class = obj_get_class(obj)
210577cb4d3eSLandon J. Fuller	if (_class == null)
210677cb4d3eSLandon J. Fuller		errorx(obj " has no superclass")
210777cb4d3eSLandon J. Fuller
210877cb4d3eSLandon J. Fuller	if (!class_has_prop_id(_class, prop_id)) {
210977cb4d3eSLandon J. Fuller		errorx("requested undefined property '" _g_props[prop_id] \
211077cb4d3eSLandon J. Fuller		    "' (" prop_id ") on " class_get_name(_class))
211177cb4d3eSLandon J. Fuller	}
211277cb4d3eSLandon J. Fuller
211377cb4d3eSLandon J. Fuller	# Track the line on which the property was set
211477cb4d3eSLandon J. Fuller	_g_obj_nr[obj,OBJ_PROP,prop_id] = NR
211577cb4d3eSLandon J. Fuller	_g_obj[obj,OBJ_PROP,prop_id] = value
211677cb4d3eSLandon J. Fuller}
211777cb4d3eSLandon J. Fuller
211877cb4d3eSLandon J. Fuller# Convert a property ID to a property path.
211977cb4d3eSLandon J. Fullerfunction prop_id_to_path(prop_id) {
212077cb4d3eSLandon J. Fuller	if (!(prop_id in _g_props))
212177cb4d3eSLandon J. Fuller		errorx("'" prop_id "' is not a property ID")
212277cb4d3eSLandon J. Fuller
212377cb4d3eSLandon J. Fuller	# Convert to path string representation
212477cb4d3eSLandon J. Fuller	return (""prop_id)
212577cb4d3eSLandon J. Fuller}
212677cb4d3eSLandon J. Fuller
212777cb4d3eSLandon J. Fuller# Convert a property to a property path.
212877cb4d3eSLandon J. Fullerfunction prop_to_path(prop) {
212977cb4d3eSLandon J. Fuller	if (!(PROP_ID in prop))
213077cb4d3eSLandon J. Fuller		errorx("prop_to_path() called with non-property head")
213177cb4d3eSLandon J. Fuller
213277cb4d3eSLandon J. Fuller	return (prop_id_to_path(prop[PROP_ID]))
213377cb4d3eSLandon J. Fuller}
213477cb4d3eSLandon J. Fuller
213577cb4d3eSLandon J. Fuller# Create a property path from head and tail properties
213677cb4d3eSLandon J. Fuller# Additional properties may be appended via prop_path_append() or
213777cb4d3eSLandon J. Fuller# prop_path_append_id()
213877cb4d3eSLandon J. Fullerfunction prop_path_create(head, tail) {
213977cb4d3eSLandon J. Fuller	if (!(PROP_ID in head))
214077cb4d3eSLandon J. Fuller		errorx("prop_path() called with non-property head")
214177cb4d3eSLandon J. Fuller
214277cb4d3eSLandon J. Fuller	if (!(PROP_ID in tail))
214377cb4d3eSLandon J. Fuller		errorx("prop_path() called with non-property tail")
214477cb4d3eSLandon J. Fuller
214577cb4d3eSLandon J. Fuller	return (head[PROP_ID] SUBSEP tail[PROP_ID])
214677cb4d3eSLandon J. Fuller}
214777cb4d3eSLandon J. Fuller
214877cb4d3eSLandon J. Fuller# Append a property to the given property path
214977cb4d3eSLandon J. Fullerfunction prop_path_append(path, tail) {
215077cb4d3eSLandon J. Fuller	if (!(PROP_ID in tail))
215177cb4d3eSLandon J. Fuller		errorx("prop_path_append() called with non-property tail")
215277cb4d3eSLandon J. Fuller
215377cb4d3eSLandon J. Fuller	return (prop_path_append_id(path, tail[PROP_ID]))
215477cb4d3eSLandon J. Fuller}
215577cb4d3eSLandon J. Fuller
215677cb4d3eSLandon J. Fuller# Append a property ID to the given property path
215777cb4d3eSLandon J. Fullerfunction prop_path_append_id(path, tail_id) {
215877cb4d3eSLandon J. Fuller	if (!(tail_id in _g_props))
215977cb4d3eSLandon J. Fuller		errorx("'" tail_id "' is not a property ID")
216077cb4d3eSLandon J. Fuller
216177cb4d3eSLandon J. Fuller	return (path SUBSEP tail_id)
216277cb4d3eSLandon J. Fuller}
216377cb4d3eSLandon J. Fuller
216477cb4d3eSLandon J. Fuller# Fetch a value from obj using a property path previously returned by
216577cb4d3eSLandon J. Fuller# prop_path_create(), prop_to_path(), etc.
216677cb4d3eSLandon J. Fullerfunction prop_get_path(obj, prop_path, _class, _prop_ids, _nprop_ids, _next,
216777cb4d3eSLandon J. Fuller    _prop_head, _prop_len, _prop_tail)
216877cb4d3eSLandon J. Fuller{
216977cb4d3eSLandon J. Fuller	if (obj == null) {
217077cb4d3eSLandon J. Fuller		errorx("requested property path '" \
217177cb4d3eSLandon J. Fuller		    gsub(SUBSEP, ".", prop_path)  "' on null object")
217277cb4d3eSLandon J. Fuller	}
217377cb4d3eSLandon J. Fuller
217477cb4d3eSLandon J. Fuller	# Try the cache first
217577cb4d3eSLandon J. Fuller	_class = obj_get_class(obj)
217677cb4d3eSLandon J. Fuller	if ((_class,prop_path,PPATH_HEAD) in _g_ppath_cache) {
217777cb4d3eSLandon J. Fuller		_prop_head = _g_ppath_cache[_class,prop_path,PPATH_HEAD]
217877cb4d3eSLandon J. Fuller		_next = prop_get(obj, _prop_head)
217977cb4d3eSLandon J. Fuller
218077cb4d3eSLandon J. Fuller		if ((_class,prop_path,PPATH_TAIL) in _g_ppath_cache) {
218177cb4d3eSLandon J. Fuller			_prop_tail = _g_ppath_cache[_class,prop_path,PPATH_TAIL]
218277cb4d3eSLandon J. Fuller			return (prop_get_path(_next, _prop_tail))
218377cb4d3eSLandon J. Fuller		}
218477cb4d3eSLandon J. Fuller
218577cb4d3eSLandon J. Fuller		return (_next)
218677cb4d3eSLandon J. Fuller	}
218777cb4d3eSLandon J. Fuller
218877cb4d3eSLandon J. Fuller	# Parse the head/tail of the property path and add to cache
218977cb4d3eSLandon J. Fuller	_nprop_ids = split(prop_path, _prop_ids, SUBSEP)
219077cb4d3eSLandon J. Fuller	if (_nprop_ids == 0)
219177cb4d3eSLandon J. Fuller		errorx("empty property path")
219277cb4d3eSLandon J. Fuller	_prop_head = _prop_ids[1]
219377cb4d3eSLandon J. Fuller	_g_ppath_cache[_class,prop_path,PPATH_HEAD] = _prop_head
219477cb4d3eSLandon J. Fuller
219577cb4d3eSLandon J. Fuller	if (_nprop_ids > 1) {
219677cb4d3eSLandon J. Fuller		_prop_len = length(_prop_head)
219777cb4d3eSLandon J. Fuller		_prop_tail = substr(prop_path, _prop_len+2)
219877cb4d3eSLandon J. Fuller
219977cb4d3eSLandon J. Fuller		# Add to cache
220077cb4d3eSLandon J. Fuller		_g_ppath_cache[_class,prop_path,PPATH_TAIL] = _prop_tail
220177cb4d3eSLandon J. Fuller	}
220277cb4d3eSLandon J. Fuller
220377cb4d3eSLandon J. Fuller	# Recursively call out implementation, this time fetching from
220477cb4d3eSLandon J. Fuller	# cache
220577cb4d3eSLandon J. Fuller	return (prop_get_path(obj, prop_path))
220677cb4d3eSLandon J. Fuller}
220777cb4d3eSLandon J. Fuller
220877cb4d3eSLandon J. Fuller# Fetch a value property value from obj, using a property ID returned by
220977cb4d3eSLandon J. Fuller# obj_get_prop_id() or class_get_prop_id()
221077cb4d3eSLandon J. Fullerfunction prop_get(obj, prop_id, _class) {
221177cb4d3eSLandon J. Fuller	if (obj == null) {
221277cb4d3eSLandon J. Fuller		errorx("requested property '" _g_props[prop_id] \
221377cb4d3eSLandon J. Fuller		    "' on null object")
221477cb4d3eSLandon J. Fuller	}
221577cb4d3eSLandon J. Fuller
221677cb4d3eSLandon J. Fuller	_class = obj_get_class(obj)
221777cb4d3eSLandon J. Fuller	if (_class == null)
221877cb4d3eSLandon J. Fuller		errorx(obj " has no superclass")
221977cb4d3eSLandon J. Fuller
222077cb4d3eSLandon J. Fuller	if (!class_has_prop_id(_class, prop_id)) {
222177cb4d3eSLandon J. Fuller		errorx("requested undefined property '" _g_props[prop_id] \
222277cb4d3eSLandon J. Fuller		    "' (" prop_id ") on " class_get_name(_class))
222377cb4d3eSLandon J. Fuller	}
222477cb4d3eSLandon J. Fuller
222577cb4d3eSLandon J. Fuller	return (_g_obj[obj,OBJ_PROP,prop_id])
222677cb4d3eSLandon J. Fuller}
222777cb4d3eSLandon J. Fuller
222877cb4d3eSLandon J. Fuller# Create a new MacroType instance
222977cb4d3eSLandon J. Fullerfunction macro_type_new(name, const_suffix, _obj) {
223077cb4d3eSLandon J. Fuller	_obj = obj_new(MacroType)
223177cb4d3eSLandon J. Fuller
223277cb4d3eSLandon J. Fuller	set(_obj, p_name, name)
223377cb4d3eSLandon J. Fuller	set(_obj, p_const_suffix, const_suffix)
223477cb4d3eSLandon J. Fuller
223577cb4d3eSLandon J. Fuller	return (_obj)
223677cb4d3eSLandon J. Fuller}
223777cb4d3eSLandon J. Fuller
223877cb4d3eSLandon J. Fuller# Create a new MacroDefine instance
223977cb4d3eSLandon J. Fullerfunction macro_new(name, value, _obj) {
224077cb4d3eSLandon J. Fuller	_obj = obj_new(MacroDefine)
224177cb4d3eSLandon J. Fuller	set(_obj, p_name, name)
224277cb4d3eSLandon J. Fuller	set(_obj, p_value, value)
224377cb4d3eSLandon J. Fuller
224477cb4d3eSLandon J. Fuller	return (_obj)
224577cb4d3eSLandon J. Fuller}
224677cb4d3eSLandon J. Fuller
224777cb4d3eSLandon J. Fuller# Create an empty array; this uses _g_arrays to store integer
224877cb4d3eSLandon J. Fuller# keys/values under the object's property prefix.
224977cb4d3eSLandon J. Fullerfunction array_new(_obj) {
225077cb4d3eSLandon J. Fuller	_obj = obj_new(Array)
225177cb4d3eSLandon J. Fuller	set(_obj, p_count, 0)
225277cb4d3eSLandon J. Fuller
225377cb4d3eSLandon J. Fuller	return (_obj)
225477cb4d3eSLandon J. Fuller}
225577cb4d3eSLandon J. Fuller
225677cb4d3eSLandon J. Fuller# Return the number of elements in the array
225777cb4d3eSLandon J. Fullerfunction array_size(array) {
225877cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
225977cb4d3eSLandon J. Fuller	return (get(array, p_count))
226077cb4d3eSLandon J. Fuller}
226177cb4d3eSLandon J. Fuller
226277cb4d3eSLandon J. Fuller# Return true if the array is empty
226377cb4d3eSLandon J. Fullerfunction array_empty(array) {
226477cb4d3eSLandon J. Fuller	return (array_size(array) == 0)
226577cb4d3eSLandon J. Fuller}
226677cb4d3eSLandon J. Fuller
226777cb4d3eSLandon J. Fuller# Append a value to the array
226877cb4d3eSLandon J. Fullerfunction array_append(array, value, _i) {
226977cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
227077cb4d3eSLandon J. Fuller
227177cb4d3eSLandon J. Fuller	_i = get(array, p_count)
227277cb4d3eSLandon J. Fuller	_g_arrays[array,OBJ_PROP,_i] = value
227377cb4d3eSLandon J. Fuller	set(array, p_count, _i+1)
227477cb4d3eSLandon J. Fuller}
227577cb4d3eSLandon J. Fuller
227677cb4d3eSLandon J. Fuller# Set an array value
227777cb4d3eSLandon J. Fuller# An error will be thrown if the idx is outside array bounds
227877cb4d3eSLandon J. Fullerfunction array_set(array, idx, value) {
227977cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
228077cb4d3eSLandon J. Fuller
228177cb4d3eSLandon J. Fuller	if (!((array,OBJ_PROP,idx) in _g_arrays))
228277cb4d3eSLandon J. Fuller		errorx(idx " out of range of array " obj_to_string(array))
228377cb4d3eSLandon J. Fuller
228477cb4d3eSLandon J. Fuller	_g_arrays[array,OBJ_PROP,idx] = value
228577cb4d3eSLandon J. Fuller}
228677cb4d3eSLandon J. Fuller
228777cb4d3eSLandon J. Fuller# Return value at the given index from the array
228877cb4d3eSLandon J. Fuller# An error will be thrown if 'idx' is outside the array bounds
228977cb4d3eSLandon J. Fullerfunction array_get(array, idx) {
229077cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
229177cb4d3eSLandon J. Fuller
229277cb4d3eSLandon J. Fuller	if (!((array,OBJ_PROP,idx) in _g_arrays))
229377cb4d3eSLandon J. Fuller		errorx(idx " out of range of array " obj_to_string(array))
229477cb4d3eSLandon J. Fuller
229577cb4d3eSLandon J. Fuller	return (_g_arrays[array,OBJ_PROP,idx])
229677cb4d3eSLandon J. Fuller}
229777cb4d3eSLandon J. Fuller
229877cb4d3eSLandon J. Fuller
229977cb4d3eSLandon J. Fuller#
230077cb4d3eSLandon J. Fuller# Sort an array, using standard awk comparison operators over its values.
230177cb4d3eSLandon J. Fuller#
2302abc551b1SLandon J. Fuller# If `prop_path*` is non-NULL, the corresponding property path (or property ID)
230377cb4d3eSLandon J. Fuller# will be fetched from each array element and used as the sorting value.
230477cb4d3eSLandon J. Fuller#
2305abc551b1SLandon J. Fuller# If multiple property paths are specified, the array is first sorted by
2306abc551b1SLandon J. Fuller# the first path, and then any equal values are sorted by the second path,
2307abc551b1SLandon J. Fuller# and so on.
2308abc551b1SLandon J. Fuller#
2309abc551b1SLandon J. Fullerfunction array_sort(array, prop_path0, prop_path1, prop_path2, _size) {
231077cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
231177cb4d3eSLandon J. Fuller
2312abc551b1SLandon J. Fuller	if (_size != null)
2313abc551b1SLandon J. Fuller		errorx("no more than three property paths may be specified")
2314abc551b1SLandon J. Fuller
231577cb4d3eSLandon J. Fuller	_size = array_size(array)
231677cb4d3eSLandon J. Fuller	if (_size <= 1)
231777cb4d3eSLandon J. Fuller		return
231877cb4d3eSLandon J. Fuller
2319abc551b1SLandon J. Fuller	_qsort(array, prop_path0, prop_path1, prop_path2, 0, _size-1)
232077cb4d3eSLandon J. Fuller}
232177cb4d3eSLandon J. Fuller
232277cb4d3eSLandon J. Fullerfunction _qsort_get_key(array, idx, prop_path, _v) {
232377cb4d3eSLandon J. Fuller	_v = array_get(array, idx)
232477cb4d3eSLandon J. Fuller
232577cb4d3eSLandon J. Fuller	if (prop_path == null)
232677cb4d3eSLandon J. Fuller		return (_v)
232777cb4d3eSLandon J. Fuller
232877cb4d3eSLandon J. Fuller	return (prop_get_path(_v, prop_path))
232977cb4d3eSLandon J. Fuller}
233077cb4d3eSLandon J. Fuller
2331abc551b1SLandon J. Fullerfunction _qsort_compare(array, lhs_idx, rhs_val, ppath0, ppath1, ppath2,
2332abc551b1SLandon J. Fuller    _lhs_val, _rhs_prop_val)
2333abc551b1SLandon J. Fuller{
2334abc551b1SLandon J. Fuller	_lhs_val = _qsort_get_key(array, lhs_idx, ppath0)
2335abc551b1SLandon J. Fuller	if (ppath0 == null)
2336abc551b1SLandon J. Fuller		_rhs_prop_val = rhs_val
2337abc551b1SLandon J. Fuller	else
2338abc551b1SLandon J. Fuller		_rhs_prop_val = prop_get_path(rhs_val, ppath0)
2339abc551b1SLandon J. Fuller
2340abc551b1SLandon J. Fuller	if (_lhs_val == _rhs_prop_val && ppath1 != null) {
2341abc551b1SLandon J. Fuller		_lhs_val = _qsort_get_key(array, lhs_idx, ppath1)
2342abc551b1SLandon J. Fuller		_rhs_prop_val = prop_get_path(rhs_val, ppath1)
2343abc551b1SLandon J. Fuller
2344abc551b1SLandon J. Fuller		if (_lhs_val == _rhs_prop_val && ppath2 != null) {
2345abc551b1SLandon J. Fuller			_lhs_val = _qsort_get_key(array, lhs_idx, ppath2)
2346abc551b1SLandon J. Fuller			_rhs_prop_val = prop_get_path(rhs_val, ppath2)
2347abc551b1SLandon J. Fuller		}
2348abc551b1SLandon J. Fuller	}
2349abc551b1SLandon J. Fuller
2350abc551b1SLandon J. Fuller	if (_lhs_val < _rhs_prop_val)
2351abc551b1SLandon J. Fuller		return (-1)
2352abc551b1SLandon J. Fuller	else if (_lhs_val > _rhs_prop_val)
2353abc551b1SLandon J. Fuller		return (1)
2354abc551b1SLandon J. Fuller	else
2355abc551b1SLandon J. Fuller		return (0)
2356abc551b1SLandon J. Fuller}
2357abc551b1SLandon J. Fuller
2358abc551b1SLandon J. Fullerfunction _qsort(array, ppath0, ppath1, ppath2, first, last, _qpivot,
2359abc551b1SLandon J. Fuller    _qleft, _qleft_val, _qright, _qright_val)
2360e83ce340SAdrian Chadd{
2361e83ce340SAdrian Chadd	if (first >= last)
2362e83ce340SAdrian Chadd		return
2363e83ce340SAdrian Chadd
2364e83ce340SAdrian Chadd	# select pivot element
2365e83ce340SAdrian Chadd	_qpivot = int(first + int((last-first+1) * rand()))
2366e83ce340SAdrian Chadd	_qleft = first
2367e83ce340SAdrian Chadd	_qright = last
2368e83ce340SAdrian Chadd
2369abc551b1SLandon J. Fuller	_qpivot_val = array_get(array, _qpivot)
2370e83ce340SAdrian Chadd
2371e83ce340SAdrian Chadd	# partition
2372e83ce340SAdrian Chadd	while (_qleft <= _qright) {
2373abc551b1SLandon J. Fuller		while (_qsort_compare(array, _qleft, _qpivot_val, ppath0, ppath1,
2374abc551b1SLandon J. Fuller		    ppath2) < 0)
2375abc551b1SLandon J. Fuller		{
2376e83ce340SAdrian Chadd			_qleft++
2377abc551b1SLandon J. Fuller		}
2378e83ce340SAdrian Chadd
2379abc551b1SLandon J. Fuller		while (_qsort_compare(array, _qright, _qpivot_val, ppath0, ppath1,
2380abc551b1SLandon J. Fuller		    ppath2) > 0)
2381abc551b1SLandon J. Fuller		{
2382e83ce340SAdrian Chadd			_qright--
2383abc551b1SLandon J. Fuller		}
2384e83ce340SAdrian Chadd
2385e83ce340SAdrian Chadd		# swap
2386e83ce340SAdrian Chadd		if (_qleft <= _qright) {
238777cb4d3eSLandon J. Fuller			_qleft_val = array_get(array, _qleft)
238877cb4d3eSLandon J. Fuller			_qright_val = array_get(array, _qright)
2389e83ce340SAdrian Chadd
239077cb4d3eSLandon J. Fuller			array_set(array, _qleft, _qright_val)
239177cb4d3eSLandon J. Fuller			array_set(array, _qright, _qleft_val)
2392e83ce340SAdrian Chadd
2393e83ce340SAdrian Chadd			_qleft++
2394e83ce340SAdrian Chadd			_qright--
2395e83ce340SAdrian Chadd		}
2396e83ce340SAdrian Chadd	}
2397e83ce340SAdrian Chadd
2398e83ce340SAdrian Chadd	# sort the partitions
2399abc551b1SLandon J. Fuller	_qsort(array, ppath0, ppath1, ppath2, first, _qright)
2400abc551b1SLandon J. Fuller	_qsort(array, ppath0, ppath1, ppath2, _qleft, last)
240177cb4d3eSLandon J. Fuller}
240277cb4d3eSLandon J. Fuller
240377cb4d3eSLandon J. Fuller
240477cb4d3eSLandon J. Fuller#
240577cb4d3eSLandon J. Fuller# Join all array values with the given separator
240677cb4d3eSLandon J. Fuller#
240777cb4d3eSLandon J. Fuller# If `prop_path` is non-NULL, the corresponding property path (or property ID)
240877cb4d3eSLandon J. Fuller# will be fetched from each array value and included in the result, rather than
240977cb4d3eSLandon J. Fuller# immediate array value
241077cb4d3eSLandon J. Fuller#
241177cb4d3eSLandon J. Fullerfunction array_join(array, sep, prop_path, _i, _size, _value, _result) {
241277cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
241377cb4d3eSLandon J. Fuller
241477cb4d3eSLandon J. Fuller	_result = ""
241577cb4d3eSLandon J. Fuller	_size = array_size(array)
241677cb4d3eSLandon J. Fuller	for (_i = 0; _i < _size; _i++) {
241777cb4d3eSLandon J. Fuller		# Fetch the value (and optionally, a target property)
241877cb4d3eSLandon J. Fuller		_value = array_get(array, _i)
241977cb4d3eSLandon J. Fuller		if (prop_path != null)
242077cb4d3eSLandon J. Fuller			_value = prop_get_path(_value, prop_path)
242177cb4d3eSLandon J. Fuller
242277cb4d3eSLandon J. Fuller		if (_i+1 < _size)
242377cb4d3eSLandon J. Fuller			_result = _result _value sep
242477cb4d3eSLandon J. Fuller		else
242577cb4d3eSLandon J. Fuller			_result = _result _value
242677cb4d3eSLandon J. Fuller	}
242777cb4d3eSLandon J. Fuller
242877cb4d3eSLandon J. Fuller	return (_result)
242977cb4d3eSLandon J. Fuller}
243077cb4d3eSLandon J. Fuller
243177cb4d3eSLandon J. Fuller# Return the first value in the array, or null if empty
243277cb4d3eSLandon J. Fullerfunction array_first(array) {
243377cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
243477cb4d3eSLandon J. Fuller
243577cb4d3eSLandon J. Fuller	if (array_size(array) == 0)
243677cb4d3eSLandon J. Fuller		return (null)
243777cb4d3eSLandon J. Fuller	else
243877cb4d3eSLandon J. Fuller		return (array_get(array, 0))
243977cb4d3eSLandon J. Fuller}
244077cb4d3eSLandon J. Fuller
244177cb4d3eSLandon J. Fuller# Return the last value in the array, or null if empty
244277cb4d3eSLandon J. Fullerfunction array_tail(list, _size) {
244377cb4d3eSLandon J. Fuller	obj_assert_class(array, Array)
244477cb4d3eSLandon J. Fuller
244577cb4d3eSLandon J. Fuller	_size = array_size(array)
244677cb4d3eSLandon J. Fuller	if (_size == 0)
244777cb4d3eSLandon J. Fuller		return (null)
244877cb4d3eSLandon J. Fuller	else
244977cb4d3eSLandon J. Fuller		return (array_get(array, _size-1))
245077cb4d3eSLandon J. Fuller}
245177cb4d3eSLandon J. Fuller
245277cb4d3eSLandon J. Fuller# Create an empty hash table; this uses the _g_maps array to store arbitrary
245377cb4d3eSLandon J. Fuller# keys/values under the object's property prefix.
245477cb4d3eSLandon J. Fullerfunction map_new(_obj) {
245577cb4d3eSLandon J. Fuller	_obj = obj_new(Map)
245677cb4d3eSLandon J. Fuller	return (_obj)
245777cb4d3eSLandon J. Fuller}
245877cb4d3eSLandon J. Fuller
245977cb4d3eSLandon J. Fuller# Add `key` with `value` to `map`
246077cb4d3eSLandon J. Fullerfunction map_set(map, key, value) {
246177cb4d3eSLandon J. Fuller	obj_assert_class(map, Map)
246277cb4d3eSLandon J. Fuller	_g_maps[map,OBJ_PROP,key] = value
246377cb4d3eSLandon J. Fuller}
246477cb4d3eSLandon J. Fuller
246577cb4d3eSLandon J. Fuller# Remove `key` from the map
246677cb4d3eSLandon J. Fullerfunction map_remove(map, key) {
246777cb4d3eSLandon J. Fuller	obj_assert_class(map, Map)
246877cb4d3eSLandon J. Fuller	delete _g_maps[map,OBJ_PROP,key]
246977cb4d3eSLandon J. Fuller}
247077cb4d3eSLandon J. Fuller
247177cb4d3eSLandon J. Fuller# Return true if `key` is found in `map`, false otherwise
247277cb4d3eSLandon J. Fullerfunction map_contains(map, key) {
247377cb4d3eSLandon J. Fuller	obj_assert_class(map, Map)
247477cb4d3eSLandon J. Fuller	return ((map,OBJ_PROP,key) in _g_maps)
247577cb4d3eSLandon J. Fuller}
247677cb4d3eSLandon J. Fuller
247777cb4d3eSLandon J. Fuller# Fetch the value of `key` from the map. Will throw an error if the
247877cb4d3eSLandon J. Fuller# key does not exist
247977cb4d3eSLandon J. Fullerfunction map_get(map, key) {
248077cb4d3eSLandon J. Fuller	obj_assert_class(map, Map)
248177cb4d3eSLandon J. Fuller	return _g_maps[map,OBJ_PROP,key]
248277cb4d3eSLandon J. Fuller}
248377cb4d3eSLandon J. Fuller
248477cb4d3eSLandon J. Fuller# Create and return a new list containing all defined values in `map`
248577cb4d3eSLandon J. Fullerfunction map_to_array(map, _key, _prefix, _values) {
248677cb4d3eSLandon J. Fuller	obj_assert_class(map, Map)
248777cb4d3eSLandon J. Fuller
248877cb4d3eSLandon J. Fuller	_values = array_new()
248977cb4d3eSLandon J. Fuller	_prefix = "^" map SUBSEP OBJ_PROP SUBSEP
249077cb4d3eSLandon J. Fuller	for (_key in _g_maps) {
249177cb4d3eSLandon J. Fuller		if (!match(_key, _prefix))
249277cb4d3eSLandon J. Fuller			continue
249377cb4d3eSLandon J. Fuller
249477cb4d3eSLandon J. Fuller		array_append(_values, _g_maps[_key])
249577cb4d3eSLandon J. Fuller	}
249677cb4d3eSLandon J. Fuller
249777cb4d3eSLandon J. Fuller	return (_values)
249877cb4d3eSLandon J. Fuller}
249977cb4d3eSLandon J. Fuller
250077cb4d3eSLandon J. Fuller# Create a new Type instance
250177cb4d3eSLandon J. Fullerfunction type_new(name, width, signed, constant, array_constant, fmt, mask,
250277cb4d3eSLandon J. Fuller    constant_value, array_constant_value, _obj)
250377cb4d3eSLandon J. Fuller{
250477cb4d3eSLandon J. Fuller	obj_assert_class(fmt, Fmt)
250577cb4d3eSLandon J. Fuller
250677cb4d3eSLandon J. Fuller	_obj = obj_new(Type)
250777cb4d3eSLandon J. Fuller	set(_obj, p_name, name)
250877cb4d3eSLandon J. Fuller	set(_obj, p_width, width)
250977cb4d3eSLandon J. Fuller	set(_obj, p_signed, signed)
251077cb4d3eSLandon J. Fuller	set(_obj, p_const, constant)
251177cb4d3eSLandon J. Fuller	set(_obj, p_const_val, constant_value)
251277cb4d3eSLandon J. Fuller	set(_obj, p_array_const, array_constant)
251377cb4d3eSLandon J. Fuller	set(_obj, p_array_const_val, array_constant_value)
251477cb4d3eSLandon J. Fuller	set(_obj, p_default_fmt, fmt)
251577cb4d3eSLandon J. Fuller	set(_obj, p_mask, mask)
251677cb4d3eSLandon J. Fuller
251777cb4d3eSLandon J. Fuller	return (_obj)
251877cb4d3eSLandon J. Fuller}
251977cb4d3eSLandon J. Fuller
252077cb4d3eSLandon J. Fuller# Return true if two types are equal
252177cb4d3eSLandon J. Fullerfunction type_equal(lhs, rhs) {
252277cb4d3eSLandon J. Fuller	# Simple case
252377cb4d3eSLandon J. Fuller	if (lhs == rhs)
252477cb4d3eSLandon J. Fuller		return (1)
252577cb4d3eSLandon J. Fuller
252677cb4d3eSLandon J. Fuller	# Must share a common class
252777cb4d3eSLandon J. Fuller	if (obj_get_class(lhs) != obj_get_class(rhs))
252877cb4d3eSLandon J. Fuller		return (0)
252977cb4d3eSLandon J. Fuller
253077cb4d3eSLandon J. Fuller	# Handle ArrayType equality
253177cb4d3eSLandon J. Fuller	if (obj_is_instanceof(lhs, ArrayType)) {
253277cb4d3eSLandon J. Fuller		# Size must be equal
253377cb4d3eSLandon J. Fuller		if (get(lhs, p_count) != get(rhs, p_count))
253477cb4d3eSLandon J. Fuller			return (0)
253577cb4d3eSLandon J. Fuller
253677cb4d3eSLandon J. Fuller		# The base types must be equal
253777cb4d3eSLandon J. Fuller		return (type_equal(type_get_base(lhs), type_get_base(rhs)))
253877cb4d3eSLandon J. Fuller	}
253977cb4d3eSLandon J. Fuller
254077cb4d3eSLandon J. Fuller	# Handle Type equality -- we just check for trivial identity
254177cb4d3eSLandon J. Fuller	# equality of all members
254277cb4d3eSLandon J. Fuller	obj_assert_class(lhs, Type)
254377cb4d3eSLandon J. Fuller	return (obj_trivially_equal(lhs, rhs))
254477cb4d3eSLandon J. Fuller}
254577cb4d3eSLandon J. Fuller
254677cb4d3eSLandon J. Fuller# Return the type's default value mask. If the type is an array type,
254777cb4d3eSLandon J. Fuller# the default mask of the base type will be returned.
254877cb4d3eSLandon J. Fullerfunction type_get_default_mask(type) {
254977cb4d3eSLandon J. Fuller	if (obj_is_instanceof(type, ArrayType))
255077cb4d3eSLandon J. Fuller		return (type_get_default_mask(type_get_base(type)))
255177cb4d3eSLandon J. Fuller
255277cb4d3eSLandon J. Fuller	obj_assert_class(type, Type)
255377cb4d3eSLandon J. Fuller	return (get(type, p_mask))
255477cb4d3eSLandon J. Fuller}
255577cb4d3eSLandon J. Fuller
255677cb4d3eSLandon J. Fuller# Return the type's C constant representation
255777cb4d3eSLandon J. Fullerfunction type_get_const(type) {
255877cb4d3eSLandon J. Fuller	if (obj_is_instanceof(type, ArrayType))
255977cb4d3eSLandon J. Fuller		return (get(type_get_base(type), p_array_const))
256077cb4d3eSLandon J. Fuller
256177cb4d3eSLandon J. Fuller	obj_assert_class(type, Type)
256277cb4d3eSLandon J. Fuller	return (get(type, p_const))
256377cb4d3eSLandon J. Fuller}
256477cb4d3eSLandon J. Fuller
256577cb4d3eSLandon J. Fuller# Return the type's C constant integer value
256677cb4d3eSLandon J. Fullerfunction type_get_const_val(type) {
256777cb4d3eSLandon J. Fuller	if (obj_is_instanceof(type, ArrayType))
256877cb4d3eSLandon J. Fuller		return (get(type_get_base(type), p_array_const_val))
256977cb4d3eSLandon J. Fuller
257077cb4d3eSLandon J. Fuller	obj_assert_class(type, Type)
257177cb4d3eSLandon J. Fuller	return (get(type, p_const_val))
257277cb4d3eSLandon J. Fuller}
257377cb4d3eSLandon J. Fuller
257477cb4d3eSLandon J. Fuller# Return an array type's element count, or 1 if the type is not
257577cb4d3eSLandon J. Fuller# an array type
257677cb4d3eSLandon J. Fullerfunction type_get_nelem(type) {
257777cb4d3eSLandon J. Fuller	if (obj_is_instanceof(type, ArrayType))
257877cb4d3eSLandon J. Fuller		return (get(type, p_count))
257977cb4d3eSLandon J. Fuller
258077cb4d3eSLandon J. Fuller	obj_assert_class(type, Type)
258177cb4d3eSLandon J. Fuller	return (1)
258277cb4d3eSLandon J. Fuller}
258377cb4d3eSLandon J. Fuller
258477cb4d3eSLandon J. Fuller# Return the base type for a given type instance.
258577cb4d3eSLandon J. Fullerfunction type_get_base(type) {
258677cb4d3eSLandon J. Fuller	if (obj_is_instanceof(type, ArrayType))
258777cb4d3eSLandon J. Fuller		return (type_get_base(get(type, p_type)))
258877cb4d3eSLandon J. Fuller
258977cb4d3eSLandon J. Fuller	obj_assert_class(type, Type)
259077cb4d3eSLandon J. Fuller	return (type)
259177cb4d3eSLandon J. Fuller}
259277cb4d3eSLandon J. Fuller
259377cb4d3eSLandon J. Fuller# Return the default fmt for a given type instance
2594c283839dSLandon J. Fullerfunction type_get_default_fmt(type, _base, _fmt, _array_fmt) {
259577cb4d3eSLandon J. Fuller	_base = type_get_base(type)
2596c283839dSLandon J. Fuller	_fmt = get(_base, p_default_fmt)
2597c283839dSLandon J. Fuller
2598c283839dSLandon J. Fuller	if (obj_is_instanceof(type, ArrayType)) {
2599c283839dSLandon J. Fuller		_array_fmt = get(_fmt, p_array_fmt)
2600c283839dSLandon J. Fuller		if (_array_fmt != null)
2601c283839dSLandon J. Fuller			_fmt = _array_fmt
2602c283839dSLandon J. Fuller	}
2603c283839dSLandon J. Fuller
2604c283839dSLandon J. Fuller	return (_fmt)
260577cb4d3eSLandon J. Fuller}
260677cb4d3eSLandon J. Fuller
260777cb4d3eSLandon J. Fuller# Return a string representation of the given type
260877cb4d3eSLandon J. Fullerfunction type_to_string(type, _base_type) {
260977cb4d3eSLandon J. Fuller	if (obj_is_instanceof(type, ArrayType)) {
261077cb4d3eSLandon J. Fuller		_base_type = type_get_base(type)
261177cb4d3eSLandon J. Fuller		return (type_to_string(_base_type) "[" get(type, p_count) "]")
261277cb4d3eSLandon J. Fuller	}
261377cb4d3eSLandon J. Fuller	return get(type, p_name)
261477cb4d3eSLandon J. Fuller}
261577cb4d3eSLandon J. Fuller
261677cb4d3eSLandon J. Fuller# Return true if type `rhs` is can be coerced to type `lhs` without data
261777cb4d3eSLandon J. Fuller# loss
261877cb4d3eSLandon J. Fullerfunction type_can_represent(lhs, rhs) {
261977cb4d3eSLandon J. Fuller	# Must be of the same class (Type or ArrayType)
262077cb4d3eSLandon J. Fuller	if (obj_get_class(lhs) != obj_get_class(rhs))
262177cb4d3eSLandon J. Fuller		return (0)
262277cb4d3eSLandon J. Fuller
262377cb4d3eSLandon J. Fuller	if (obj_is_instanceof(lhs, ArrayType)) {
262477cb4d3eSLandon J. Fuller		# The base types must have a representable relationship
262577cb4d3eSLandon J. Fuller		if (!type_can_represent(type_get_base(lhs), type_get_base(rhs)))
262677cb4d3eSLandon J. Fuller			return (0)
262777cb4d3eSLandon J. Fuller
262877cb4d3eSLandon J. Fuller		# The lhs type must be able to represent -at least- as
262977cb4d3eSLandon J. Fuller		# many elements as the RHS type
263077cb4d3eSLandon J. Fuller		if (get(lhs, p_count) < get(rhs, p_count))
263177cb4d3eSLandon J. Fuller			return (0)
263277cb4d3eSLandon J. Fuller
263377cb4d3eSLandon J. Fuller		return (1)
263477cb4d3eSLandon J. Fuller	}
263577cb4d3eSLandon J. Fuller
263677cb4d3eSLandon J. Fuller	# A signed type could represent the full range of a smaller unsigned
263777cb4d3eSLandon J. Fuller	# type, but we don't bother; the two should agree when used in a SROM
263877cb4d3eSLandon J. Fuller	# layout. Instead simply assert that both are signed or unsigned.
263977cb4d3eSLandon J. Fuller	if (get(lhs, p_signed) != get(rhs, p_signed))
264077cb4d3eSLandon J. Fuller		return (0)
264177cb4d3eSLandon J. Fuller
264277cb4d3eSLandon J. Fuller	# The `rhs` type must be equal or smaller in width to the `lhs` type
264377cb4d3eSLandon J. Fuller	if (get(lhs, p_width) < get(rhs, p_width))
264477cb4d3eSLandon J. Fuller		return (0)
264577cb4d3eSLandon J. Fuller
264677cb4d3eSLandon J. Fuller	return (1)
264777cb4d3eSLandon J. Fuller}
264877cb4d3eSLandon J. Fuller
264977cb4d3eSLandon J. Fuller# Create a new ArrayType instance
265077cb4d3eSLandon J. Fullerfunction array_type_new(type, count, _obj) {
265177cb4d3eSLandon J. Fuller	_obj = obj_new(ArrayType)
265277cb4d3eSLandon J. Fuller	set(_obj, p_type, type)
265377cb4d3eSLandon J. Fuller	set(_obj, p_count, count)
265477cb4d3eSLandon J. Fuller
265577cb4d3eSLandon J. Fuller	return (_obj)
265677cb4d3eSLandon J. Fuller}
265777cb4d3eSLandon J. Fuller
265877cb4d3eSLandon J. Fuller#
265977cb4d3eSLandon J. Fuller# Parse a type string to either the Type, ArrayType, or null if
266077cb4d3eSLandon J. Fuller# the type is not recognized.
266177cb4d3eSLandon J. Fuller#
266277cb4d3eSLandon J. Fullerfunction parse_type_string(str, _base, _count) {
266377cb4d3eSLandon J. Fuller	if (match(str, ARRAY_REGEX"$") > 0) {
266477cb4d3eSLandon J. Fuller		# Extract count and base type
266577cb4d3eSLandon J. Fuller		_count = substr(str, RSTART+1, RLENGTH-2)
266677cb4d3eSLandon J. Fuller		sub(ARRAY_REGEX"$", "", str)
266777cb4d3eSLandon J. Fuller
266877cb4d3eSLandon J. Fuller		# Look for base type
266977cb4d3eSLandon J. Fuller		if ((_base = type_named(str)) == null)
267077cb4d3eSLandon J. Fuller			return (null)
267177cb4d3eSLandon J. Fuller
267277cb4d3eSLandon J. Fuller		return (array_type_new(_base, int(_count)))
267377cb4d3eSLandon J. Fuller	} else {
267477cb4d3eSLandon J. Fuller		return (type_named(str))
267577cb4d3eSLandon J. Fuller	}
267677cb4d3eSLandon J. Fuller}
267777cb4d3eSLandon J. Fuller
267877cb4d3eSLandon J. Fuller#
267977cb4d3eSLandon J. Fuller# Parse a variable name in the form of 'name' or 'name[len]', returning
268077cb4d3eSLandon J. Fuller# either the provided base_type if no array specifiers are found, or
268177cb4d3eSLandon J. Fuller# the fully parsed ArrayType.
268277cb4d3eSLandon J. Fuller#
268377cb4d3eSLandon J. Fullerfunction parse_array_type_specifier(str, base_type, _count) {
268477cb4d3eSLandon J. Fuller	if (match(str, ARRAY_REGEX"$") > 0) {
268577cb4d3eSLandon J. Fuller		# Extract count
268677cb4d3eSLandon J. Fuller		_count = substr(str, RSTART+1, RLENGTH-2)
268777cb4d3eSLandon J. Fuller		return (array_type_new(base_type, int(_count)))
268877cb4d3eSLandon J. Fuller	} else {
268977cb4d3eSLandon J. Fuller		return (base_type)
269077cb4d3eSLandon J. Fuller	}
269177cb4d3eSLandon J. Fuller}
269277cb4d3eSLandon J. Fuller
269377cb4d3eSLandon J. Fuller# Return the type constant for `name`, if any
269477cb4d3eSLandon J. Fullerfunction type_named(name, _n, _type) {
269577cb4d3eSLandon J. Fuller	if (name == null)
269677cb4d3eSLandon J. Fuller		errorx("called type_named() with null name")
269777cb4d3eSLandon J. Fuller
269877cb4d3eSLandon J. Fuller	if (map_contains(BaseTypes, name))
269977cb4d3eSLandon J. Fuller		return (map_get(BaseTypes, name))
270077cb4d3eSLandon J. Fuller
270177cb4d3eSLandon J. Fuller	return (null)
270277cb4d3eSLandon J. Fuller}
270377cb4d3eSLandon J. Fuller
270477cb4d3eSLandon J. Fuller# Create a new Fmt instance
2705c283839dSLandon J. Fullerfunction fmt_new(name, symbol, array_fmt, _obj) {
270677cb4d3eSLandon J. Fuller	_obj = obj_new(Fmt)
270777cb4d3eSLandon J. Fuller	set(_obj, p_name, name)
270877cb4d3eSLandon J. Fuller	set(_obj, p_symbol, symbol)
270977cb4d3eSLandon J. Fuller
2710c283839dSLandon J. Fuller	if (array_fmt != null)
2711c283839dSLandon J. Fuller		set(_obj, p_array_fmt, array_fmt)
2712c283839dSLandon J. Fuller
271377cb4d3eSLandon J. Fuller	return (_obj)
271477cb4d3eSLandon J. Fuller}
271577cb4d3eSLandon J. Fuller
271677cb4d3eSLandon J. Fuller
271777cb4d3eSLandon J. Fuller# Return the Fmt constant for `name`, if any
271877cb4d3eSLandon J. Fullerfunction fmt_named(name, _n, _fmt) {
271977cb4d3eSLandon J. Fuller	if (map_contains(ValueFormats, name))
272077cb4d3eSLandon J. Fuller		return (map_get(ValueFormats, name))
272177cb4d3eSLandon J. Fuller
272277cb4d3eSLandon J. Fuller	return (null)
272377cb4d3eSLandon J. Fuller}
272477cb4d3eSLandon J. Fuller
272577cb4d3eSLandon J. Fuller# Create a new VFlag instance
272677cb4d3eSLandon J. Fullerfunction vflag_new(name, constant, _obj) {
272777cb4d3eSLandon J. Fuller	_obj = obj_new(VFlag)
272877cb4d3eSLandon J. Fuller	set(_obj, p_name, name)
272977cb4d3eSLandon J. Fuller	set(_obj, p_const, constant)
273077cb4d3eSLandon J. Fuller
273177cb4d3eSLandon J. Fuller	return (_obj)
273277cb4d3eSLandon J. Fuller}
273377cb4d3eSLandon J. Fuller
273477cb4d3eSLandon J. Fuller# Create a new StringConstant AST node
273577cb4d3eSLandon J. Fullerfunction stringconstant_new(value, continued, _obj) {
273677cb4d3eSLandon J. Fuller	_obj = obj_new(StringConstant)
273777cb4d3eSLandon J. Fuller	set(_obj, p_value, value)
273877cb4d3eSLandon J. Fuller	set(_obj, p_continued, continued)
273977cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
274077cb4d3eSLandon J. Fuller
274177cb4d3eSLandon J. Fuller	return (_obj)
274277cb4d3eSLandon J. Fuller}
274377cb4d3eSLandon J. Fuller
274477cb4d3eSLandon J. Fuller# Create an empty StringConstant AST node to which additional lines
274577cb4d3eSLandon J. Fuller# may be appended
274677cb4d3eSLandon J. Fullerfunction stringconstant_empty(_obj) {
274777cb4d3eSLandon J. Fuller	return (stringconstant_new("", 1))
274877cb4d3eSLandon J. Fuller}
274977cb4d3eSLandon J. Fuller
275077cb4d3eSLandon J. Fuller# Parse an input string and return a new string constant
275177cb4d3eSLandon J. Fuller# instance
275277cb4d3eSLandon J. Fullerfunction stringconstant_parse_line(line, _obj) {
275377cb4d3eSLandon J. Fuller	_obj = stringconstant_empty()
275477cb4d3eSLandon J. Fuller	stringconstant_append_line(_obj, line)
275577cb4d3eSLandon J. Fuller	return (_obj)
275677cb4d3eSLandon J. Fuller}
275777cb4d3eSLandon J. Fuller
275877cb4d3eSLandon J. Fuller# Parse and apend an additional line to this string constant
275977cb4d3eSLandon J. Fullerfunction stringconstant_append_line(str, line, _cont, _strbuf, _regex, _eol) {
276077cb4d3eSLandon J. Fuller	obj_assert_class(str, StringConstant)
276177cb4d3eSLandon J. Fuller
276277cb4d3eSLandon J. Fuller	# Must be marked for continuation
276377cb4d3eSLandon J. Fuller	if (!get(str, p_continued)) {
276477cb4d3eSLandon J. Fuller		errorx("can't append to non-continuation string '" \
276577cb4d3eSLandon J. Fuller		    get(str, p_value) "'")
276677cb4d3eSLandon J. Fuller	}
276777cb4d3eSLandon J. Fuller
276877cb4d3eSLandon J. Fuller	_strbuf = get(str, p_value)
276977cb4d3eSLandon J. Fuller
277077cb4d3eSLandon J. Fuller	# If start of string, look for (and remove) initial double quote
277177cb4d3eSLandon J. Fuller	if (_strbuf == null) {
277277cb4d3eSLandon J. Fuller		_regex = "^[ \t]*\""
277377cb4d3eSLandon J. Fuller		if (!sub(_regex, "", line)) {
277477cb4d3eSLandon J. Fuller			error("expected quoted string")
277577cb4d3eSLandon J. Fuller		}
277677cb4d3eSLandon J. Fuller	}
277777cb4d3eSLandon J. Fuller
277877cb4d3eSLandon J. Fuller	# Look for a terminating double quote
277977cb4d3eSLandon J. Fuller	_regex = "([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\""
278077cb4d3eSLandon J. Fuller
278177cb4d3eSLandon J. Fuller	_eol = match(line, _regex)
278277cb4d3eSLandon J. Fuller	if (_eol > 0) {
278377cb4d3eSLandon J. Fuller		# Drop everything following the terminating quote
278477cb4d3eSLandon J. Fuller		line = substr(line, 1, RLENGTH-1)
278577cb4d3eSLandon J. Fuller		_cont = 0
278677cb4d3eSLandon J. Fuller	} else {
278777cb4d3eSLandon J. Fuller		# No terminating quote found, continues on next line
278877cb4d3eSLandon J. Fuller		_cont = 1
278977cb4d3eSLandon J. Fuller	}
279077cb4d3eSLandon J. Fuller
279177cb4d3eSLandon J. Fuller	# Trim leading and trailing whitespace
279277cb4d3eSLandon J. Fuller	sub(/(^[ \t]+|[ \t]+$)/, "", line)
279377cb4d3eSLandon J. Fuller
279477cb4d3eSLandon J. Fuller	# Append to existing buffer
279577cb4d3eSLandon J. Fuller	if ((_strbuf = get(str, p_value)) == NULL)
279677cb4d3eSLandon J. Fuller		set(str, p_value, line)
279777cb4d3eSLandon J. Fuller	else
279877cb4d3eSLandon J. Fuller		set(str, p_value, _strbuf " " line)
279977cb4d3eSLandon J. Fuller
280077cb4d3eSLandon J. Fuller	# Update line continuation setting
280177cb4d3eSLandon J. Fuller	set(str, p_continued, _cont)
280277cb4d3eSLandon J. Fuller}
280377cb4d3eSLandon J. Fuller
280477cb4d3eSLandon J. Fuller# Create a new RevRange instance
280577cb4d3eSLandon J. Fullerfunction revrange_new(start, end, _obj) {
280677cb4d3eSLandon J. Fuller	_obj = obj_new(RevRange)
280777cb4d3eSLandon J. Fuller	set(_obj, p_start, start)
280877cb4d3eSLandon J. Fuller	set(_obj, p_end, end)
280977cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
281077cb4d3eSLandon J. Fuller
281177cb4d3eSLandon J. Fuller	return (_obj)
281277cb4d3eSLandon J. Fuller}
281377cb4d3eSLandon J. Fuller
281477cb4d3eSLandon J. Fuller# Return true if the two revision ranges are equal
281577cb4d3eSLandon J. Fullerfunction revrange_equal(lhs, rhs) {
281677cb4d3eSLandon J. Fuller	if (get(lhs, p_start) != get(rhs, p_start))
281777cb4d3eSLandon J. Fuller		return (0)
281877cb4d3eSLandon J. Fuller
281977cb4d3eSLandon J. Fuller	if (get(lhs, p_end) != get(rhs, p_end))
282077cb4d3eSLandon J. Fuller		return (0)
282177cb4d3eSLandon J. Fuller
282277cb4d3eSLandon J. Fuller	return (1)
282377cb4d3eSLandon J. Fuller}
282477cb4d3eSLandon J. Fuller
282577cb4d3eSLandon J. Fuller# Return true if the requested rev is covered by revrange, false otherwise
282677cb4d3eSLandon J. Fullerfunction revrange_contains(range, rev) {
282777cb4d3eSLandon J. Fuller	obj_assert_class(range, RevRange)
282877cb4d3eSLandon J. Fuller
282977cb4d3eSLandon J. Fuller	if (rev < get(range, p_start))
283077cb4d3eSLandon J. Fuller		return (0)
283177cb4d3eSLandon J. Fuller	else if (rev > get(range, p_end)) {
283277cb4d3eSLandon J. Fuller		return (0)
283377cb4d3eSLandon J. Fuller	} else {
283477cb4d3eSLandon J. Fuller		return (1)
283577cb4d3eSLandon J. Fuller	}
283677cb4d3eSLandon J. Fuller}
283777cb4d3eSLandon J. Fuller
283877cb4d3eSLandon J. Fuller#
283977cb4d3eSLandon J. Fuller# Return a string representation of the given revision range
284077cb4d3eSLandon J. Fuller#
284177cb4d3eSLandon J. Fullerfunction revrange_to_string(revs, _start, _end) {
284277cb4d3eSLandon J. Fuller	obj_assert_class(revs, RevRange)
284377cb4d3eSLandon J. Fuller
284477cb4d3eSLandon J. Fuller	_start = get(revs, p_start)
284577cb4d3eSLandon J. Fuller	_end = get(revs, p_end)
284677cb4d3eSLandon J. Fuller
284777cb4d3eSLandon J. Fuller	if (_start == 0)
284877cb4d3eSLandon J. Fuller		return ("<= " _end)
284977cb4d3eSLandon J. Fuller	else if (_end == REV_MAX)
285077cb4d3eSLandon J. Fuller		return (">= " _start)
285177cb4d3eSLandon J. Fuller	else
285277cb4d3eSLandon J. Fuller		return (_start "-" _end)
285377cb4d3eSLandon J. Fuller}
285477cb4d3eSLandon J. Fuller
285577cb4d3eSLandon J. Fuller# Create a new VarGroup instance
285677cb4d3eSLandon J. Fullerfunction var_group_new(name, _obj) {
285777cb4d3eSLandon J. Fuller	_obj = obj_new(VarGroup)
285877cb4d3eSLandon J. Fuller	set(_obj, p_name, name)
285977cb4d3eSLandon J. Fuller	set(_obj, p_vars, array_new())
286077cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
286177cb4d3eSLandon J. Fuller
286277cb4d3eSLandon J. Fuller	return (_obj)
286377cb4d3eSLandon J. Fuller}
286477cb4d3eSLandon J. Fuller
286577cb4d3eSLandon J. Fuller# Create a new NVRAM instance
286677cb4d3eSLandon J. Fullerfunction nvram_new(_obj, _vars, _v) {
286777cb4d3eSLandon J. Fuller	_obj = obj_new(NVRAM)
286877cb4d3eSLandon J. Fuller	_vars = array_new()
286977cb4d3eSLandon J. Fuller	set(_obj, p_vars, _vars)
287077cb4d3eSLandon J. Fuller	set(_obj, p_var_groups, array_new())
287177cb4d3eSLandon J. Fuller	set(_obj, p_srom_layouts, array_new())
287277cb4d3eSLandon J. Fuller	set(_obj, p_srom_table, map_new())
287377cb4d3eSLandon J. Fuller
287477cb4d3eSLandon J. Fuller	#
287577cb4d3eSLandon J. Fuller	# Register our implicit variable definitions
287677cb4d3eSLandon J. Fuller	#
287777cb4d3eSLandon J. Fuller
287877cb4d3eSLandon J. Fuller	# SROM signature offset
287977cb4d3eSLandon J. Fuller	_v = var_new(VAccessInternal, "<sromsig>", UInt16)
288077cb4d3eSLandon J. Fuller	array_append(_vars, _v)
288177cb4d3eSLandon J. Fuller	_g_var_names[get(_v, p_name)] = _v
288277cb4d3eSLandon J. Fuller
288377cb4d3eSLandon J. Fuller	# SROM CRC8 offset
288477cb4d3eSLandon J. Fuller	_v = var_new(VAccessInternal, "<sromcrc>", UInt8)
288577cb4d3eSLandon J. Fuller	array_append(_vars, _v)
288677cb4d3eSLandon J. Fuller	_g_var_names[get(_v, p_name)] = _v
288777cb4d3eSLandon J. Fuller
288877cb4d3eSLandon J. Fuller	return (_obj)
288977cb4d3eSLandon J. Fuller}
289077cb4d3eSLandon J. Fuller
289177cb4d3eSLandon J. Fuller# Register a new SROM layout instance
289277cb4d3eSLandon J. Fuller# An error will be thrown if the layout overlaps any revisions covered
289377cb4d3eSLandon J. Fuller# by an existing instance.
289477cb4d3eSLandon J. Fullerfunction nvram_add_srom_layout(nvram, layout, _table, _revs, _start, _end, _i) {
289577cb4d3eSLandon J. Fuller	obj_assert_class(nvram, NVRAM)
289677cb4d3eSLandon J. Fuller	obj_assert_class(layout, SromLayout)
289777cb4d3eSLandon J. Fuller
289877cb4d3eSLandon J. Fuller	# revision:layout hash table
289977cb4d3eSLandon J. Fuller	_table = get(nvram, p_srom_table)
290077cb4d3eSLandon J. Fuller
290177cb4d3eSLandon J. Fuller	# register the layout's revisions
290277cb4d3eSLandon J. Fuller	_revs = get(layout, p_revisions)
290377cb4d3eSLandon J. Fuller	_start = get(_revs, p_start)
290477cb4d3eSLandon J. Fuller	_end = get(_revs, p_end)
290577cb4d3eSLandon J. Fuller
290677cb4d3eSLandon J. Fuller	for (_i = _start; _i <= _end; _i++) {
290777cb4d3eSLandon J. Fuller		if (map_contains(_table, _i)) {
290877cb4d3eSLandon J. Fuller			error("SROM layout redeclares layout for revision '" \
290977cb4d3eSLandon J. Fuller			    _i "' (originally declared on line " \
291077cb4d3eSLandon J. Fuller			    get(map_get(_table, _i), p_line) ")")
291177cb4d3eSLandon J. Fuller		}
291277cb4d3eSLandon J. Fuller
291377cb4d3eSLandon J. Fuller		map_set(_table, _i, layout)
291477cb4d3eSLandon J. Fuller	}
291577cb4d3eSLandon J. Fuller
291677cb4d3eSLandon J. Fuller	# append to srom_layouts
291777cb4d3eSLandon J. Fuller	array_append(get(nvram, p_srom_layouts), layout)
291877cb4d3eSLandon J. Fuller}
291977cb4d3eSLandon J. Fuller
292077cb4d3eSLandon J. Fuller# Return the first SROM layout registered for a given SROM revision,
292177cb4d3eSLandon J. Fuller# or null if no matching layout is found
292277cb4d3eSLandon J. Fullerfunction nvram_get_srom_layout(nvram, revision, _layouts, _nlayouts, _layout,
292377cb4d3eSLandon J. Fuller    _i)
292477cb4d3eSLandon J. Fuller{
292577cb4d3eSLandon J. Fuller	obj_assert_class(nvram, NVRAM)
292677cb4d3eSLandon J. Fuller
292777cb4d3eSLandon J. Fuller	_layouts = get(nvram, p_srom_layouts)
292877cb4d3eSLandon J. Fuller	_nlayouts = array_size(_layouts)
292977cb4d3eSLandon J. Fuller	for (_i = 0; _i < _nlayouts; _i++) {
293077cb4d3eSLandon J. Fuller		_layout = array_get(_layouts, _i)
293177cb4d3eSLandon J. Fuller
293277cb4d3eSLandon J. Fuller		if (srom_layout_has_rev(_layout, revision))
293377cb4d3eSLandon J. Fuller			return (_layout)
293477cb4d3eSLandon J. Fuller	}
293577cb4d3eSLandon J. Fuller
293677cb4d3eSLandon J. Fuller	# Not found
293777cb4d3eSLandon J. Fuller	return (null)
293877cb4d3eSLandon J. Fuller}
293977cb4d3eSLandon J. Fuller
294077cb4d3eSLandon J. Fuller# Create a new Var instance
294177cb4d3eSLandon J. Fullerfunction var_new(access, name, type, _obj) {
294277cb4d3eSLandon J. Fuller	obj_assert_class(access, VAccess)
294377cb4d3eSLandon J. Fuller
294477cb4d3eSLandon J. Fuller	# Validate the variable identifier
294577cb4d3eSLandon J. Fuller	#
294677cb4d3eSLandon J. Fuller	# The access modifier dictates the permitted identifier format.
294777cb4d3eSLandon J. Fuller	#   VAccessInternal:		<ident>
294877cb4d3eSLandon J. Fuller	#   VAccess(Public|Private):	ident
294977cb4d3eSLandon J. Fuller	if (access != VAccessInternal && name ~ SVAR_IDENT_REGEX) {
295077cb4d3eSLandon J. Fuller		error("invalid identifier '"name"'; did you mean to " \
295177cb4d3eSLandon J. Fuller		    "mark this variable as internal?")
295277cb4d3eSLandon J. Fuller	} else if (access == VAccessInternal) {
295377cb4d3eSLandon J. Fuller		if (name !~ SVAR_IDENT_REGEX)
295477cb4d3eSLandon J. Fuller			error("invalid identifier '"name"' for internal " \
295577cb4d3eSLandon J. Fuller			"variable; did you mean '<" name ">'?")
295677cb4d3eSLandon J. Fuller	} else if (name !~ VAR_IDENT_REGEX) {
295777cb4d3eSLandon J. Fuller		error("invalid identifier '"name"'")
295877cb4d3eSLandon J. Fuller	}
295977cb4d3eSLandon J. Fuller
296077cb4d3eSLandon J. Fuller	_obj = obj_new(Var)
296177cb4d3eSLandon J. Fuller	set(_obj, p_access, access)
296277cb4d3eSLandon J. Fuller	set(_obj, p_name, name)
296377cb4d3eSLandon J. Fuller	set(_obj, p_type, type)
296477cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
296577cb4d3eSLandon J. Fuller
296677cb4d3eSLandon J. Fuller	return (_obj)
296777cb4d3eSLandon J. Fuller}
296877cb4d3eSLandon J. Fuller
296977cb4d3eSLandon J. Fuller# Return true if var is internal-only, and should not be included
297077cb4d3eSLandon J. Fuller# in any output (e.g. has an access specifier of VAccessInternal).
297177cb4d3eSLandon J. Fullerfunction var_is_internal(var) {
297277cb4d3eSLandon J. Fuller	return (get(var, p_access) == VAccessInternal)
297377cb4d3eSLandon J. Fuller}
297477cb4d3eSLandon J. Fuller
297577cb4d3eSLandon J. Fuller# Return true if `var` has an array type
297677cb4d3eSLandon J. Fullerfunction var_has_array_type(var, _vtype) {
297777cb4d3eSLandon J. Fuller	obj_assert_class(var, Var)
297877cb4d3eSLandon J. Fuller	_vtype = get(var, p_type)
297977cb4d3eSLandon J. Fuller	return (obj_is_instanceof(_vtype, ArrayType))
298077cb4d3eSLandon J. Fuller}
298177cb4d3eSLandon J. Fuller
298277cb4d3eSLandon J. Fuller# Return the number of array elements defined by this variable's type,
298377cb4d3eSLandon J. Fuller# or 1 if the variable does not have an array type.
298477cb4d3eSLandon J. Fullerfunction var_get_array_len(var) {
298577cb4d3eSLandon J. Fuller	obj_assert_class(var, Var)
298677cb4d3eSLandon J. Fuller	return (type_get_nelem(get(var, p_type)))
298777cb4d3eSLandon J. Fuller}
298877cb4d3eSLandon J. Fuller
298977cb4d3eSLandon J. Fuller# Return the fmt for var. If not explicitly set on var, will return then
299077cb4d3eSLandon J. Fuller# return of calling type_get_default_fmt() with the variable's type
299177cb4d3eSLandon J. Fullerfunction var_get_fmt(var, _fmt) {
299277cb4d3eSLandon J. Fuller	obj_assert_class(var, Var)
299377cb4d3eSLandon J. Fuller
299477cb4d3eSLandon J. Fuller	# If defined on var, return it
299577cb4d3eSLandon J. Fuller	if ((_fmt = get(var, p_fmt)) != null)
299677cb4d3eSLandon J. Fuller		return (_fmt)
299777cb4d3eSLandon J. Fuller
299877cb4d3eSLandon J. Fuller	# Fall back on the type's default
299977cb4d3eSLandon J. Fuller	return (type_get_default_fmt(get(var, p_type)))
300077cb4d3eSLandon J. Fuller}
300177cb4d3eSLandon J. Fuller
300277cb4d3eSLandon J. Fuller# Return a new MacroDefine instance for the given variable, macro type,
300377cb4d3eSLandon J. Fuller# and value
300477cb4d3eSLandon J. Fullerfunction var_get_macro(var, macro_type, value, _macro) {
300577cb4d3eSLandon J. Fuller	obj_assert_class(var, Var)
300677cb4d3eSLandon J. Fuller	obj_assert_class(macro_type, MacroType)
300777cb4d3eSLandon J. Fuller
300877cb4d3eSLandon J. Fuller	return (macro_new(var_get_macro_name(var, macro_type), value))
300977cb4d3eSLandon J. Fuller}
301077cb4d3eSLandon J. Fuller
301177cb4d3eSLandon J. Fuller# Return the preprocessor constant name to be used with `var` for the given
301277cb4d3eSLandon J. Fuller# macro_type
301377cb4d3eSLandon J. Fullerfunction var_get_macro_name(var, macro_type, _var_name, _suffix) {
301477cb4d3eSLandon J. Fuller	obj_assert_class(var, Var)
301577cb4d3eSLandon J. Fuller	obj_assert_class(macro_type, MacroType)
301677cb4d3eSLandon J. Fuller
301777cb4d3eSLandon J. Fuller	_var_name = get(var, p_name)
301877cb4d3eSLandon J. Fuller	_suffix = get(macro_type, p_const_suffix)
301977cb4d3eSLandon J. Fuller
302077cb4d3eSLandon J. Fuller	return("BHND_NVAR_" toupper(_var_name) _suffix)
302177cb4d3eSLandon J. Fuller}
302277cb4d3eSLandon J. Fuller
302377cb4d3eSLandon J. Fuller# Create a new SromLayout instance
302477cb4d3eSLandon J. Fullerfunction srom_layout_new(rev_desc, _obj)
302577cb4d3eSLandon J. Fuller{
302677cb4d3eSLandon J. Fuller	_obj = obj_new(SromLayout)
302777cb4d3eSLandon J. Fuller	set(_obj, p_revisions, rev_desc)
302877cb4d3eSLandon J. Fuller	set(_obj, p_entries, array_new())
302977cb4d3eSLandon J. Fuller	set(_obj, p_revmap, map_new())
303077cb4d3eSLandon J. Fuller	set(_obj, p_output_var_counts, map_new())
303177cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
303277cb4d3eSLandon J. Fuller
303377cb4d3eSLandon J. Fuller	return (_obj)
303477cb4d3eSLandon J. Fuller}
303577cb4d3eSLandon J. Fuller
303677cb4d3eSLandon J. Fuller# Register a new entry with the srom layout
303777cb4d3eSLandon J. Fullerfunction srom_layout_add_entry(layout, entry, _revmap, _name, _rev_start,
303877cb4d3eSLandon J. Fuller    _rev_end, _var, _prev_entry, _count, _i)
303977cb4d3eSLandon J. Fuller{
304077cb4d3eSLandon J. Fuller	obj_assert_class(layout, SromLayout)
304177cb4d3eSLandon J. Fuller	obj_assert_class(entry, SromEntry)
304277cb4d3eSLandon J. Fuller
304377cb4d3eSLandon J. Fuller	_layout_revmap = get(layout, p_revmap)
304477cb4d3eSLandon J. Fuller	_layout_var_count = get(layout, p_output_var_counts)
304577cb4d3eSLandon J. Fuller
304677cb4d3eSLandon J. Fuller	_var = get(entry, p_var)
304777cb4d3eSLandon J. Fuller	_name = get(_var, p_name)
304877cb4d3eSLandon J. Fuller
304977cb4d3eSLandon J. Fuller	# Add to revision array
305077cb4d3eSLandon J. Fuller	array_append(get(layout, p_entries), entry)
305177cb4d3eSLandon J. Fuller
305277cb4d3eSLandon J. Fuller	# Add to the revision map tables
305377cb4d3eSLandon J. Fuller	_rev_start = get(get(entry, p_revisions), p_start)
305477cb4d3eSLandon J. Fuller	_rev_end = get(get(entry, p_revisions), p_end)
305577cb4d3eSLandon J. Fuller
305677cb4d3eSLandon J. Fuller	for (_i = _rev_start; _i <= _rev_end; _i++) {
305777cb4d3eSLandon J. Fuller		# Check for existing entry
305877cb4d3eSLandon J. Fuller		_prev_entry = srom_layout_find_entry(layout, _name, _i)
305977cb4d3eSLandon J. Fuller		if (_prev_entry != null) {
306077cb4d3eSLandon J. Fuller			error("redefinition of variable '" _name "' for SROM " \
306177cb4d3eSLandon J. Fuller			    "revision " _i " (previously defined on line " \
306277cb4d3eSLandon J. Fuller			    get(_prev_entry, p_line) ")")
306377cb4d3eSLandon J. Fuller		}
306477cb4d3eSLandon J. Fuller
306577cb4d3eSLandon J. Fuller		# Add to the (varname,revision) map
306677cb4d3eSLandon J. Fuller		map_set(_layout_revmap, (_name SUBSEP _i), entry)
306777cb4d3eSLandon J. Fuller
306877cb4d3eSLandon J. Fuller		# If an output variable, set or increment the output variable
306977cb4d3eSLandon J. Fuller		# count
307077cb4d3eSLandon J. Fuller		if (!srom_entry_should_output(entry, _i))
307177cb4d3eSLandon J. Fuller			continue
307277cb4d3eSLandon J. Fuller
307377cb4d3eSLandon J. Fuller		if (!map_contains(_layout_var_count, _i)) {
307477cb4d3eSLandon J. Fuller			map_set(_layout_var_count, _i, 1)
307577cb4d3eSLandon J. Fuller		} else {
307677cb4d3eSLandon J. Fuller			_count = map_get(_layout_var_count, _i)
307777cb4d3eSLandon J. Fuller			map_set(_layout_var_count, _i, _count + 1)
307877cb4d3eSLandon J. Fuller		}
307977cb4d3eSLandon J. Fuller	}
308077cb4d3eSLandon J. Fuller}
308177cb4d3eSLandon J. Fuller
308277cb4d3eSLandon J. Fuller
308377cb4d3eSLandon J. Fuller# Return the variable name to be used when emitting C declarations
308477cb4d3eSLandon J. Fuller# for this SROM layout
308577cb4d3eSLandon J. Fuller#
308677cb4d3eSLandon J. Fuller# The name is gauranteed to be unique across SROM layouts with non-overlapping
308777cb4d3eSLandon J. Fuller# revision ranges
308877cb4d3eSLandon J. Fullerfunction srom_layout_get_variable_name(layout, _revs) {
308977cb4d3eSLandon J. Fuller	obj_assert_class(layout, SromLayout)
309077cb4d3eSLandon J. Fuller
309177cb4d3eSLandon J. Fuller	_revs = get(layout, p_revisions)
309277cb4d3eSLandon J. Fuller
309377cb4d3eSLandon J. Fuller	return ("bhnd_sprom_layout_r" get(_revs, p_start) \
309477cb4d3eSLandon J. Fuller	    "_r" get(_revs, p_end))
309577cb4d3eSLandon J. Fuller}
309677cb4d3eSLandon J. Fuller
309777cb4d3eSLandon J. Fuller# Return true if the given SROM revision is defined by the layout, false
309877cb4d3eSLandon J. Fuller# otherwise
309977cb4d3eSLandon J. Fullerfunction srom_layout_has_rev(layout, rev) {
310077cb4d3eSLandon J. Fuller	obj_assert_class(layout, SromLayout)
310177cb4d3eSLandon J. Fuller	return (revrange_contains(get(layout, p_revisions), rev))
310277cb4d3eSLandon J. Fuller}
310377cb4d3eSLandon J. Fuller
310477cb4d3eSLandon J. Fuller
310577cb4d3eSLandon J. Fuller# Return the total number of output variables (variables to be included
310677cb4d3eSLandon J. Fuller# in the SROM layout bindings) for the given SROM revision
310777cb4d3eSLandon J. Fullerfunction srom_layout_num_output_vars(layout, rev, _counts)
310877cb4d3eSLandon J. Fuller{
310977cb4d3eSLandon J. Fuller	obj_assert_class(layout, SromLayout)
311077cb4d3eSLandon J. Fuller
311177cb4d3eSLandon J. Fuller	_counts = get(layout, p_output_var_counts)
311277cb4d3eSLandon J. Fuller	if (!map_contains(_counts, rev))
311377cb4d3eSLandon J. Fuller		return (0)
311477cb4d3eSLandon J. Fuller
311577cb4d3eSLandon J. Fuller	return (map_get(_counts, rev))
311677cb4d3eSLandon J. Fuller}
311777cb4d3eSLandon J. Fuller
311877cb4d3eSLandon J. Fuller# Return the SromEntry defined for the given variable name and SROM revision,
311977cb4d3eSLandon J. Fuller# or null if none
312077cb4d3eSLandon J. Fullerfunction srom_layout_find_entry(layout, vname, revision, _key, _srom_revmap) {
312177cb4d3eSLandon J. Fuller	obj_assert_class(layout, SromLayout)
312277cb4d3eSLandon J. Fuller
312377cb4d3eSLandon J. Fuller	_srom_revmap = get(layout, p_revmap)
312477cb4d3eSLandon J. Fuller
312577cb4d3eSLandon J. Fuller	# SromEntry are mapped by name,revision composite keys
312677cb4d3eSLandon J. Fuller	_key = vname SUBSEP revision
312777cb4d3eSLandon J. Fuller	if (!map_contains(_srom_revmap, _key))
312877cb4d3eSLandon J. Fuller		return (null)
312977cb4d3eSLandon J. Fuller
313077cb4d3eSLandon J. Fuller	return (map_get(_srom_revmap, _key))
313177cb4d3eSLandon J. Fuller
313277cb4d3eSLandon J. Fuller}
313377cb4d3eSLandon J. Fuller
313477cb4d3eSLandon J. Fuller# Create a new SromLayoutFilter instance, checking that `revs`
313577cb4d3eSLandon J. Fuller# falls within the parent's revision range
313677cb4d3eSLandon J. Fullerfunction srom_layout_filter_new(parent, revs, _obj, _start, _end, _parent_revs) {
313777cb4d3eSLandon J. Fuller	obj_assert_class(parent, SromLayout)
313877cb4d3eSLandon J. Fuller	obj_assert_class(revs, RevRange)
313977cb4d3eSLandon J. Fuller
314077cb4d3eSLandon J. Fuller	# Fetch our parent's revision range, confirm that we're
314177cb4d3eSLandon J. Fuller	# a strict subset
314277cb4d3eSLandon J. Fuller	_start = get(revs, p_start)
314377cb4d3eSLandon J. Fuller	_end = get(revs, p_end)
314477cb4d3eSLandon J. Fuller	_parent_revs = get(parent, p_revisions)
314577cb4d3eSLandon J. Fuller
314677cb4d3eSLandon J. Fuller	if (!revrange_contains(_parent_revs, _start))
314777cb4d3eSLandon J. Fuller		error("'" _start "' is outside of parent range")
314877cb4d3eSLandon J. Fuller
314977cb4d3eSLandon J. Fuller	if (!revrange_contains(_parent_revs, _end))
315077cb4d3eSLandon J. Fuller		error("'" _end "' is outside of parent range")
315177cb4d3eSLandon J. Fuller
315277cb4d3eSLandon J. Fuller	if (revrange_equal(revs, _parent_revs)) {
315377cb4d3eSLandon J. Fuller		error("srom range '" revrange_to_string(revs) "' is " \
315477cb4d3eSLandon J. Fuller		    "identical to parent range of '" \
315577cb4d3eSLandon J. Fuller		        revrange_to_string(_parent_revs) "'")
315677cb4d3eSLandon J. Fuller	}
315777cb4d3eSLandon J. Fuller
315877cb4d3eSLandon J. Fuller	# Construct and return new filter instance
315977cb4d3eSLandon J. Fuller	_obj = obj_new(SromLayoutFilter)
316077cb4d3eSLandon J. Fuller	set(_obj, p_parent, parent)
316177cb4d3eSLandon J. Fuller	set(_obj, p_revisions, revs)
316277cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
316377cb4d3eSLandon J. Fuller
316477cb4d3eSLandon J. Fuller	return (_obj)
316577cb4d3eSLandon J. Fuller}
316677cb4d3eSLandon J. Fuller
316777cb4d3eSLandon J. Fuller#
316877cb4d3eSLandon J. Fuller# Create a new SromEntry instance
316977cb4d3eSLandon J. Fuller#
317077cb4d3eSLandon J. Fuller# var:		The variable referenced by this entry
317177cb4d3eSLandon J. Fuller# revisions:	The SROM revisions to which this entry applies
317277cb4d3eSLandon J. Fuller# base_offset:	The SROM entry offset; any relative segment offsets will be
317377cb4d3eSLandon J. Fuller#		calculated relative to the base offset
317477cb4d3eSLandon J. Fuller# type:		The SROM's value type; this may be a subtype of the variable
317577cb4d3eSLandon J. Fuller#		type, and defines the data (width, sign, etc) to be read from
317677cb4d3eSLandon J. Fuller#		SROM.
317777cb4d3eSLandon J. Fuller#
317877cb4d3eSLandon J. Fullerfunction srom_entry_new(var, revisions, base_offset, type, _obj) {
317977cb4d3eSLandon J. Fuller	obj_assert_class(var, Var)
318077cb4d3eSLandon J. Fuller	if (revisions != null)
318177cb4d3eSLandon J. Fuller		obj_assert_class(revisions, RevRange)
318277cb4d3eSLandon J. Fuller
318377cb4d3eSLandon J. Fuller	_obj = obj_new(SromEntry)
318477cb4d3eSLandon J. Fuller	set(_obj, p_var, var)
318577cb4d3eSLandon J. Fuller	set(_obj, p_revisions, revisions)
318677cb4d3eSLandon J. Fuller	set(_obj, p_base_offset, base_offset)
318777cb4d3eSLandon J. Fuller	set(_obj, p_type, type)
318877cb4d3eSLandon J. Fuller	set(_obj, p_offsets, array_new())
318977cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
319077cb4d3eSLandon J. Fuller
319177cb4d3eSLandon J. Fuller	return (_obj)
319277cb4d3eSLandon J. Fuller}
319377cb4d3eSLandon J. Fuller
319477cb4d3eSLandon J. Fuller# Return true if the SromEntry has an array type
319577cb4d3eSLandon J. Fullerfunction srom_entry_has_array_type(entry) {
319677cb4d3eSLandon J. Fuller	obj_assert_class(entry, SromEntry)
319777cb4d3eSLandon J. Fuller
319877cb4d3eSLandon J. Fuller	return (obj_is_instanceof(get(entry, p_type), ArrayType))
319977cb4d3eSLandon J. Fuller}
320077cb4d3eSLandon J. Fuller
320177cb4d3eSLandon J. Fuller# Return the number of array elements defined by this SromEntry's type,
320277cb4d3eSLandon J. Fuller# or 1 if the entry does not have an array type.
320377cb4d3eSLandon J. Fullerfunction srom_entry_get_array_len(entry, _type) {
320477cb4d3eSLandon J. Fuller	obj_assert_class(entry, SromEntry)
320577cb4d3eSLandon J. Fuller
320677cb4d3eSLandon J. Fuller	return (type_get_nelem(get(entry, p_type)))
320777cb4d3eSLandon J. Fuller}
320877cb4d3eSLandon J. Fuller
320977cb4d3eSLandon J. Fuller#
321077cb4d3eSLandon J. Fuller# Return true if the given entry should be included in the output bindings
321177cb4d3eSLandon J. Fuller# generated for the given revision, false otherwise.
321277cb4d3eSLandon J. Fuller#
321377cb4d3eSLandon J. Fullerfunction srom_entry_should_output(entry, rev, _var, _revs)
321477cb4d3eSLandon J. Fuller{
321577cb4d3eSLandon J. Fuller	obj_assert_class(entry, SromEntry)
321677cb4d3eSLandon J. Fuller
321777cb4d3eSLandon J. Fuller	_var = get(entry, p_var)
321877cb4d3eSLandon J. Fuller	_revs = get(entry, p_revisions)
321977cb4d3eSLandon J. Fuller
322077cb4d3eSLandon J. Fuller	# Exclude internal variables
322177cb4d3eSLandon J. Fuller	if (var_is_internal(_var))
322277cb4d3eSLandon J. Fuller		return (0)
322377cb4d3eSLandon J. Fuller
322477cb4d3eSLandon J. Fuller	# Exclude inapplicable entry revisions
322577cb4d3eSLandon J. Fuller	if (!revrange_contains(_revs, rev))
322677cb4d3eSLandon J. Fuller		return (0)
322777cb4d3eSLandon J. Fuller
322877cb4d3eSLandon J. Fuller	return (1)
322977cb4d3eSLandon J. Fuller}
323077cb4d3eSLandon J. Fuller
323177cb4d3eSLandon J. Fuller#
323277cb4d3eSLandon J. Fuller# Return the single, non-shifted, non-masked offset/segment for the given
323377cb4d3eSLandon J. Fuller# SromEntry, or throw an error if the entry contains multiple offsets/segments.
323477cb4d3eSLandon J. Fuller#
323577cb4d3eSLandon J. Fuller# This is used to fetch special-cased variable definitions that are required
323677cb4d3eSLandon J. Fuller# to present a single simple offset.
323777cb4d3eSLandon J. Fuller#
323877cb4d3eSLandon J. Fullerfunction srom_entry_get_single_segment(entry, _offsets, _segments, _seg,
323977cb4d3eSLandon J. Fuller    _base_type, _default_mask)
324077cb4d3eSLandon J. Fuller{
324177cb4d3eSLandon J. Fuller	obj_assert_class(entry, SromEntry)
324277cb4d3eSLandon J. Fuller
324377cb4d3eSLandon J. Fuller	# Fetch the single offset's segment list
324477cb4d3eSLandon J. Fuller	_offsets = get(entry, p_offsets)
324577cb4d3eSLandon J. Fuller	if (array_size(_offsets) != 1)
324677cb4d3eSLandon J. Fuller		errorc(get(entry, p_line), "unsupported offset count")
324777cb4d3eSLandon J. Fuller
324877cb4d3eSLandon J. Fuller	_segments = get(array_first(_offsets), p_segments)
324977cb4d3eSLandon J. Fuller	if (array_size(_segments) != 1)
325077cb4d3eSLandon J. Fuller		errorc(get(entry, p_line), "unsupported segment count")
325177cb4d3eSLandon J. Fuller
325277cb4d3eSLandon J. Fuller	# Fetch the single segment
325377cb4d3eSLandon J. Fuller	_seg = array_first(_segments)
325477cb4d3eSLandon J. Fuller	_base_type = srom_segment_get_base_type(_seg)
325577cb4d3eSLandon J. Fuller	_default_mask = get(_base_type, p_mask)
325677cb4d3eSLandon J. Fuller
325777cb4d3eSLandon J. Fuller	# Must not be shifted/masked
325877cb4d3eSLandon J. Fuller	if (get(_seg, p_shift) != 0)
325977cb4d3eSLandon J. Fuller		errorc(obj_get_prop_nr(_seg, p_mask), "shift unsupported")
326077cb4d3eSLandon J. Fuller
326177cb4d3eSLandon J. Fuller	if (get(_seg, p_mask) != _default_mask)
326277cb4d3eSLandon J. Fuller		errorc(obj_get_prop_nr(_seg, p_mask), "mask unsupported")
326377cb4d3eSLandon J. Fuller
326477cb4d3eSLandon J. Fuller	return  (_seg)
326577cb4d3eSLandon J. Fuller}
326677cb4d3eSLandon J. Fuller
326777cb4d3eSLandon J. Fuller# Create a new SromOffset instance
326877cb4d3eSLandon J. Fullerfunction srom_offset_new(_obj) {
326977cb4d3eSLandon J. Fuller	_obj = obj_new(SromOffset)
327077cb4d3eSLandon J. Fuller	set(_obj, p_segments, array_new())
327177cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
327277cb4d3eSLandon J. Fuller
327377cb4d3eSLandon J. Fuller	return (_obj)
327477cb4d3eSLandon J. Fuller}
327577cb4d3eSLandon J. Fuller
327677cb4d3eSLandon J. Fuller# Return the number of SromSegment instances defined by this offset.
327777cb4d3eSLandon J. Fullerfunction srom_offset_segment_count(offset) {
327877cb4d3eSLandon J. Fuller	obj_assert_class(offset, SromOffset)
327977cb4d3eSLandon J. Fuller	return (array_size(get(offset, p_segments)))
328077cb4d3eSLandon J. Fuller}
328177cb4d3eSLandon J. Fuller
328277cb4d3eSLandon J. Fuller# Return the idx'th segment. Will throw an error if idx falls outside
328377cb4d3eSLandon J. Fuller# the number of available segments.
328477cb4d3eSLandon J. Fullerfunction srom_offset_get_segment(offset, idx, _segments, _seg) {
328577cb4d3eSLandon J. Fuller	obj_assert_class(offset, SromOffset)
328677cb4d3eSLandon J. Fuller
328777cb4d3eSLandon J. Fuller	return (array_get(get(offset, p_segments), idx))
328877cb4d3eSLandon J. Fuller}
328977cb4d3eSLandon J. Fuller
329077cb4d3eSLandon J. Fuller# Create a new SromSegment instance
329177cb4d3eSLandon J. Fullerfunction srom_segment_new(offset, type, mask, shift, value, _obj) {
329277cb4d3eSLandon J. Fuller	_obj = obj_new(SromSegment)
329377cb4d3eSLandon J. Fuller	set(_obj, p_offset, offset)
329477cb4d3eSLandon J. Fuller	set(_obj, p_type, type)
329577cb4d3eSLandon J. Fuller	set(_obj, p_mask, mask)
329677cb4d3eSLandon J. Fuller	set(_obj, p_shift, shift)
329777cb4d3eSLandon J. Fuller	set(_obj, p_value, value)
329877cb4d3eSLandon J. Fuller	set(_obj, p_line, NR)
329977cb4d3eSLandon J. Fuller
330077cb4d3eSLandon J. Fuller	return (_obj)
330177cb4d3eSLandon J. Fuller}
330277cb4d3eSLandon J. Fuller
330377cb4d3eSLandon J. Fuller# Return true if the segment has an array type
330477cb4d3eSLandon J. Fullerfunction srom_segment_has_array_type(seg, _type) {
330577cb4d3eSLandon J. Fuller	_type = srom_segment_get_type(seg)
330677cb4d3eSLandon J. Fuller	return (obj_is_instanceof(_type, ArrayType))
330777cb4d3eSLandon J. Fuller}
330877cb4d3eSLandon J. Fuller
330977cb4d3eSLandon J. Fuller# Return the array count of the segment, or 1 if the segment does not have
331077cb4d3eSLandon J. Fuller# an array type
331177cb4d3eSLandon J. Fullerfunction srom_segment_get_array_len(seg, _type) {
331277cb4d3eSLandon J. Fuller	if (!srom_segment_has_array_type(seg))
331377cb4d3eSLandon J. Fuller		return (1)
331477cb4d3eSLandon J. Fuller
331577cb4d3eSLandon J. Fuller	_type = srom_segment_get_type(seg)
331677cb4d3eSLandon J. Fuller	return (get(_type, p_count))
331777cb4d3eSLandon J. Fuller}
331877cb4d3eSLandon J. Fuller
331977cb4d3eSLandon J. Fuller# Return the type of the segment
332077cb4d3eSLandon J. Fullerfunction srom_segment_get_type(seg) {
332177cb4d3eSLandon J. Fuller	obj_assert_class(seg, SromSegment)
332277cb4d3eSLandon J. Fuller	return (get(seg, p_type))
332377cb4d3eSLandon J. Fuller
332477cb4d3eSLandon J. Fuller}
332577cb4d3eSLandon J. Fuller
332677cb4d3eSLandon J. Fuller# Return the base type of the segment
332777cb4d3eSLandon J. Fullerfunction srom_segment_get_base_type(seg) {
332877cb4d3eSLandon J. Fuller	return (type_get_base(srom_segment_get_type(seg)))
332977cb4d3eSLandon J. Fuller}
333077cb4d3eSLandon J. Fuller
333177cb4d3eSLandon J. Fuller# Return true if the two segments have identical types and attributes (i.e.
333277cb4d3eSLandon J. Fuller# differing only by offset)
333377cb4d3eSLandon J. Fullerfunction srom_segment_attributes_equal(lhs, rhs) {
333477cb4d3eSLandon J. Fuller	obj_assert_class(lhs, SromSegment)
333577cb4d3eSLandon J. Fuller	obj_assert_class(rhs, SromSegment)
333677cb4d3eSLandon J. Fuller
333777cb4d3eSLandon J. Fuller	# type
333877cb4d3eSLandon J. Fuller	if (!type_equal(get(lhs, p_type), get(rhs, p_type)))
333977cb4d3eSLandon J. Fuller		return (0)
334077cb4d3eSLandon J. Fuller
334177cb4d3eSLandon J. Fuller	# mask
334277cb4d3eSLandon J. Fuller	if (get(lhs, p_mask) != get(rhs, p_mask))
334377cb4d3eSLandon J. Fuller		return (0)
334477cb4d3eSLandon J. Fuller
334577cb4d3eSLandon J. Fuller	# shift
334677cb4d3eSLandon J. Fuller	if (get(lhs, p_shift) != get(rhs, p_shift))
334777cb4d3eSLandon J. Fuller		return (0)
334877cb4d3eSLandon J. Fuller
334977cb4d3eSLandon J. Fuller	# value
335077cb4d3eSLandon J. Fuller	if (get(lhs, p_value) != get(rhs, p_value))
335177cb4d3eSLandon J. Fuller		return (0)
335277cb4d3eSLandon J. Fuller
335377cb4d3eSLandon J. Fuller	return (1)
335477cb4d3eSLandon J. Fuller}
335577cb4d3eSLandon J. Fuller
335677cb4d3eSLandon J. Fuller# Return a human-readable representation of a Segment instance
335777cb4d3eSLandon J. Fullerfunction segment_to_string(seg, _str, _t, _m, _s,  _attrs, _attr_str) {
335877cb4d3eSLandon J. Fuller	_attrs = array_new()
335977cb4d3eSLandon J. Fuller
336077cb4d3eSLandon J. Fuller	# include type (if specified)
336177cb4d3eSLandon J. Fuller	if ((_t = get(seg, p_type)) != null)
336277cb4d3eSLandon J. Fuller		_str = (type_to_string(_t) " ")
336377cb4d3eSLandon J. Fuller
336477cb4d3eSLandon J. Fuller	# include offset
336577cb4d3eSLandon J. Fuller	_str = (_str sprintf("0x%X", get(seg, p_offset)))
336677cb4d3eSLandon J. Fuller
336777cb4d3eSLandon J. Fuller	# append list of attributes
336877cb4d3eSLandon J. Fuller	if ((_m = get(seg, p_mask)) != null)
336977cb4d3eSLandon J. Fuller		array_append(_attrs, ("&" _m))
337077cb4d3eSLandon J. Fuller
337177cb4d3eSLandon J. Fuller	if ((_s = get(seg, p_shift)) != null) {
337277cb4d3eSLandon J. Fuller		if (_s > 0)
337377cb4d3eSLandon J. Fuller			_s = ">>" _s
337477cb4d3eSLandon J. Fuller		else
337577cb4d3eSLandon J. Fuller			_s = "<<" _s
337677cb4d3eSLandon J. Fuller		array_append(_attrs, _s)
337777cb4d3eSLandon J. Fuller	}
337877cb4d3eSLandon J. Fuller
337977cb4d3eSLandon J. Fuller	_attr_str = array_join(_attrs, ", ")
338077cb4d3eSLandon J. Fuller	obj_delete(_attrs)
338177cb4d3eSLandon J. Fuller
338277cb4d3eSLandon J. Fuller	if (_attr_str == "")
338377cb4d3eSLandon J. Fuller		return (_str)
338477cb4d3eSLandon J. Fuller	else
338577cb4d3eSLandon J. Fuller		return (_str " (" _attr_str ")")
338677cb4d3eSLandon J. Fuller}
338777cb4d3eSLandon J. Fuller
338877cb4d3eSLandon J. Fuller# return the flag definition for variable `v`
338977cb4d3eSLandon J. Fullerfunction gen_var_flags(v, _type, _flags, _flag, _str)
339077cb4d3eSLandon J. Fuller{
339177cb4d3eSLandon J. Fuller	_num_flags = 0;
339277cb4d3eSLandon J. Fuller	_type = get(v, p_type)
339377cb4d3eSLandon J. Fuller	_flags = array_new()
339477cb4d3eSLandon J. Fuller
339577cb4d3eSLandon J. Fuller	# VF_PRIVATE
339677cb4d3eSLandon J. Fuller	if (get(v, p_access) == VAccessPrivate)
339777cb4d3eSLandon J. Fuller		array_append(_flags, VFlagPrivate)
339877cb4d3eSLandon J. Fuller
339977cb4d3eSLandon J. Fuller	# VF_IGNALL1
340077cb4d3eSLandon J. Fuller	if (get(v, p_ignall1))
340177cb4d3eSLandon J. Fuller		array_append(_flags, VFlagIgnoreAll1)
340277cb4d3eSLandon J. Fuller
340377cb4d3eSLandon J. Fuller	# If empty, return empty flag value
340477cb4d3eSLandon J. Fuller	if (array_size(_flags) == 0) {
340577cb4d3eSLandon J. Fuller		obj_delete(_flags)
340677cb4d3eSLandon J. Fuller		return ("0")
340777cb4d3eSLandon J. Fuller	}
340877cb4d3eSLandon J. Fuller
340977cb4d3eSLandon J. Fuller	# Join all flag constants with |
341077cb4d3eSLandon J. Fuller	_str = array_join(_flags, "|", class_get_prop_id(VFlag, p_const))
341177cb4d3eSLandon J. Fuller
341277cb4d3eSLandon J. Fuller	# Clean up
341377cb4d3eSLandon J. Fuller	obj_delete(_flags)
341477cb4d3eSLandon J. Fuller
341577cb4d3eSLandon J. Fuller	return (_str)
341677cb4d3eSLandon J. Fuller}
341777cb4d3eSLandon J. Fuller
341877cb4d3eSLandon J. Fuller#
341977cb4d3eSLandon J. Fuller# Return the absolute value
342077cb4d3eSLandon J. Fuller#
342177cb4d3eSLandon J. Fullerfunction abs(i) {
342277cb4d3eSLandon J. Fuller	return (i < 0 ? -i : i)
342377cb4d3eSLandon J. Fuller}
342477cb4d3eSLandon J. Fuller
342577cb4d3eSLandon J. Fuller#
342677cb4d3eSLandon J. Fuller# Return the minimum of two values
342777cb4d3eSLandon J. Fuller#
342877cb4d3eSLandon J. Fullerfunction min(lhs, rhs) {
342977cb4d3eSLandon J. Fuller	return (lhs < rhs ? lhs : rhs)
343077cb4d3eSLandon J. Fuller}
343177cb4d3eSLandon J. Fuller
343277cb4d3eSLandon J. Fuller#
343377cb4d3eSLandon J. Fuller# Return the maximum of two values
343477cb4d3eSLandon J. Fuller#
343577cb4d3eSLandon J. Fullerfunction max(lhs, rhs) {
343677cb4d3eSLandon J. Fuller	return (lhs > rhs ? lhs : rhs)
343777cb4d3eSLandon J. Fuller}
343877cb4d3eSLandon J. Fuller
343977cb4d3eSLandon J. Fuller#
344077cb4d3eSLandon J. Fuller# Parse a hex string
344177cb4d3eSLandon J. Fuller#
344277cb4d3eSLandon J. Fullerfunction parse_hex_string(str, _hex_pstate, _out, _p, _count) {
344377cb4d3eSLandon J. Fuller	if (!AWK_REQ_HEX_PARSING)
344477cb4d3eSLandon J. Fuller		return (str + 0)
344577cb4d3eSLandon J. Fuller
344677cb4d3eSLandon J. Fuller	# Populate hex parsing lookup table on-demand
344777cb4d3eSLandon J. Fuller	if (!("F" in _g_hex_table)) {
344877cb4d3eSLandon J. Fuller		for (_p = 0; _p < 16; _p++) {
344977cb4d3eSLandon J. Fuller			_g_hex_table[sprintf("%X", _p)] = _p
345077cb4d3eSLandon J. Fuller			_g_hex_table[sprintf("%x", _p)] = _p
345177cb4d3eSLandon J. Fuller		}
345277cb4d3eSLandon J. Fuller	}
345377cb4d3eSLandon J. Fuller
345477cb4d3eSLandon J. Fuller	# Split input into an array
345577cb4d3eSLandon J. Fuller	_count = split(toupper(str), _hex_pstate, "")
345677cb4d3eSLandon J. Fuller	_p = 1
345777cb4d3eSLandon J. Fuller
345877cb4d3eSLandon J. Fuller	# Skip leading '0x'
345977cb4d3eSLandon J. Fuller	if (_count >= 2 && _hex_pstate[1] == "0") {
346077cb4d3eSLandon J. Fuller		if (_hex_pstate[2] == "x" || _hex_pstate[2] == "X")
346177cb4d3eSLandon J. Fuller			_p += 2
346277cb4d3eSLandon J. Fuller	}
346377cb4d3eSLandon J. Fuller
346477cb4d3eSLandon J. Fuller	# Parse the hex_digits
346577cb4d3eSLandon J. Fuller	_out = 0
346677cb4d3eSLandon J. Fuller	for (; _p <= _count; _p++)
346777cb4d3eSLandon J. Fuller		_out = (_out * 16) + _g_hex_table[_hex_pstate[_p]]
346877cb4d3eSLandon J. Fuller
346977cb4d3eSLandon J. Fuller	return (_out)
347077cb4d3eSLandon J. Fuller}
347177cb4d3eSLandon J. Fuller
347277cb4d3eSLandon J. Fuller#
347377cb4d3eSLandon J. Fuller# Return the integer representation of an unsigned decimal, hexadecimal, or
347477cb4d3eSLandon J. Fuller# octal string
347577cb4d3eSLandon J. Fuller#
347677cb4d3eSLandon J. Fullerfunction parse_uint_string(str) {
347777cb4d3eSLandon J. Fuller	if (str ~ UINT_REGEX)
347877cb4d3eSLandon J. Fuller		return (int(str))
347977cb4d3eSLandon J. Fuller	else if (str ~ HEX_REGEX)
348077cb4d3eSLandon J. Fuller		return (parse_hex_string(str))
348177cb4d3eSLandon J. Fuller	else
348277cb4d3eSLandon J. Fuller		error("invalid integer value: '" str "'")
348377cb4d3eSLandon J. Fuller}
348477cb4d3eSLandon J. Fuller
348577cb4d3eSLandon J. Fuller#
348677cb4d3eSLandon J. Fuller# Parse an offset string, stripping any leading '+' or trailing ':' or ','
348777cb4d3eSLandon J. Fuller# characters
348877cb4d3eSLandon J. Fuller#
348977cb4d3eSLandon J. Fuller# +0x0:
349077cb4d3eSLandon J. Fuller# 0x0,
349177cb4d3eSLandon J. Fuller# ...
349277cb4d3eSLandon J. Fuller#
349377cb4d3eSLandon J. Fullerfunction parse_uint_offset(str) {
349477cb4d3eSLandon J. Fuller	# Drop any leading '+'
349577cb4d3eSLandon J. Fuller	sub(/^\+/, "", str)
349677cb4d3eSLandon J. Fuller
349777cb4d3eSLandon J. Fuller	# Drop any trailing ':', ',', or '|'
349877cb4d3eSLandon J. Fuller	sub("[,|:]$", "", str)
349977cb4d3eSLandon J. Fuller
350077cb4d3eSLandon J. Fuller	# Parse the cleaned up string
350177cb4d3eSLandon J. Fuller	return (parse_uint_string(str))
3502e83ce340SAdrian Chadd}
3503e83ce340SAdrian Chadd
3504e83ce340SAdrian Chadd#
3505e83ce340SAdrian Chadd# Print msg to output file, without indentation
3506e83ce340SAdrian Chadd#
350777cb4d3eSLandon J. Fullerfunction emit_ni(msg) {
3508e83ce340SAdrian Chadd	printf("%s", msg) >> OUTPUT_FILE
3509e83ce340SAdrian Chadd}
3510e83ce340SAdrian Chadd
3511e83ce340SAdrian Chadd#
3512e83ce340SAdrian Chadd# Print msg to output file, indented for the current `output_depth`
3513e83ce340SAdrian Chadd#
351477cb4d3eSLandon J. Fullerfunction emit(msg, _ind) {
3515e83ce340SAdrian Chadd	for (_ind = 0; _ind < output_depth; _ind++)
3516e83ce340SAdrian Chadd		emit_ni("\t")
3517e83ce340SAdrian Chadd
3518e83ce340SAdrian Chadd	emit_ni(msg)
3519e83ce340SAdrian Chadd}
3520e83ce340SAdrian Chadd
3521e83ce340SAdrian Chadd#
3522e83ce340SAdrian Chadd# Print a warning to stderr
3523e83ce340SAdrian Chadd#
352477cb4d3eSLandon J. Fullerfunction warn(msg) {
3525e83ce340SAdrian Chadd	print "warning:", msg, "at", FILENAME, "line", NR > "/dev/stderr"
3526e83ce340SAdrian Chadd}
3527e83ce340SAdrian Chadd
3528e83ce340SAdrian Chadd#
352977cb4d3eSLandon J. Fuller# Print an warning message without including the source line information
353077cb4d3eSLandon J. Fuller#
353177cb4d3eSLandon J. Fullerfunction warnx(msg) {
353277cb4d3eSLandon J. Fuller	print "warning:", msg > "/dev/stderr"
353377cb4d3eSLandon J. Fuller}
353477cb4d3eSLandon J. Fuller
353577cb4d3eSLandon J. Fuller#
353677cb4d3eSLandon J. Fuller# Print a compiler error to stderr with a caller supplied
353777cb4d3eSLandon J. Fuller# line number
353877cb4d3eSLandon J. Fuller#
353977cb4d3eSLandon J. Fullerfunction errorc(line, msg) {
354077cb4d3eSLandon J. Fuller	errorx(msg " at " FILENAME " line " line)
354177cb4d3eSLandon J. Fuller}
354277cb4d3eSLandon J. Fuller
354377cb4d3eSLandon J. Fuller#
3544e83ce340SAdrian Chadd# Print a compiler error to stderr
3545e83ce340SAdrian Chadd#
354677cb4d3eSLandon J. Fullerfunction error(msg) {
3547e83ce340SAdrian Chadd	errorx(msg " at " FILENAME " line " NR ":\n\t" $0)
3548e83ce340SAdrian Chadd}
3549e83ce340SAdrian Chadd
3550e83ce340SAdrian Chadd#
3551e83ce340SAdrian Chadd# Print an error message without including the source line information
3552e83ce340SAdrian Chadd#
355377cb4d3eSLandon J. Fullerfunction errorx(msg) {
3554e83ce340SAdrian Chadd	print "error:", msg > "/dev/stderr"
3555e83ce340SAdrian Chadd	_EARLY_EXIT=1
3556e83ce340SAdrian Chadd	exit 1
3557e83ce340SAdrian Chadd}
3558e83ce340SAdrian Chadd
3559e83ce340SAdrian Chadd#
3560e83ce340SAdrian Chadd# Print a debug output message
3561e83ce340SAdrian Chadd#
356277cb4d3eSLandon J. Fullerfunction debug(msg, _i) {
3563e83ce340SAdrian Chadd	if (!DEBUG)
3564e83ce340SAdrian Chadd		return
356577cb4d3eSLandon J. Fuller	for (_i = 1; _i < _g_parse_stack_depth; _i++)
3566e83ce340SAdrian Chadd		printf("\t") > "/dev/stderr"
3567e83ce340SAdrian Chadd	print msg > "/dev/stderr"
3568e83ce340SAdrian Chadd}
3569e83ce340SAdrian Chadd
3570e83ce340SAdrian Chadd#
3571e83ce340SAdrian Chadd# Advance to the next non-comment input record
3572e83ce340SAdrian Chadd#
357377cb4d3eSLandon J. Fullerfunction next_line(_result) {
3574e83ce340SAdrian Chadd	do {
3575e83ce340SAdrian Chadd		_result = getline
3576e83ce340SAdrian Chadd	} while (_result > 0 && $0 ~ /^[ \t]*#.*/) # skip comment lines
3577e83ce340SAdrian Chadd	return (_result)
3578e83ce340SAdrian Chadd}
3579e83ce340SAdrian Chadd
3580e83ce340SAdrian Chadd#
3581e83ce340SAdrian Chadd# Advance to the next input record and verify that it matches @p regex
3582e83ce340SAdrian Chadd#
358377cb4d3eSLandon J. Fullerfunction getline_matching(regex, _result) {
3584e83ce340SAdrian Chadd	_result = next_line()
3585e83ce340SAdrian Chadd	if (_result <= 0)
3586e83ce340SAdrian Chadd		return (_result)
3587e83ce340SAdrian Chadd
3588e83ce340SAdrian Chadd	if ($0 ~ regex)
3589e83ce340SAdrian Chadd		return (1)
3590e83ce340SAdrian Chadd
3591e83ce340SAdrian Chadd	return (-1)
3592e83ce340SAdrian Chadd}
3593e83ce340SAdrian Chadd
3594e83ce340SAdrian Chadd#
3595e83ce340SAdrian Chadd# Shift the current fields left by `n`.
3596e83ce340SAdrian Chadd#
3597e83ce340SAdrian Chadd# If all fields are consumed and the optional do_getline argument is true,
3598e83ce340SAdrian Chadd# read the next line.
3599e83ce340SAdrian Chadd#
360077cb4d3eSLandon J. Fullerfunction shiftf(n, do_getline, _i) {
360177cb4d3eSLandon J. Fuller	if (n > NF)
360277cb4d3eSLandon J. Fuller		error("shift past end of line")
360377cb4d3eSLandon J. Fuller
360477cb4d3eSLandon J. Fuller	if (n == NF) {
360577cb4d3eSLandon J. Fuller		# If shifting the entire line, just reset the line value
360677cb4d3eSLandon J. Fuller		$0 = ""
360777cb4d3eSLandon J. Fuller	} else {
360877cb4d3eSLandon J. Fuller		for (_i = 1; _i <= NF-n; _i++) {
360977cb4d3eSLandon J. Fuller			$(_i) = $(_i+n)
3610e83ce340SAdrian Chadd		}
3611e83ce340SAdrian Chadd		NF = NF - n
361277cb4d3eSLandon J. Fuller	}
3613e83ce340SAdrian Chadd
3614e83ce340SAdrian Chadd	if (NF == 0 && do_getline)
3615e83ce340SAdrian Chadd		next_line()
3616e83ce340SAdrian Chadd}
3617e83ce340SAdrian Chadd
3618e83ce340SAdrian Chadd# Push a new parser state.
361977cb4d3eSLandon J. Fullerfunction parser_state_push(ctx, is_block, _state) {
362077cb4d3eSLandon J. Fuller	_state = obj_new(ParseState)
362177cb4d3eSLandon J. Fuller	set(_state, p_ctx, ctx)
362277cb4d3eSLandon J. Fuller	set(_state, p_is_block, is_block)
362377cb4d3eSLandon J. Fuller	set(_state, p_line, NR)
362477cb4d3eSLandon J. Fuller
362577cb4d3eSLandon J. Fuller	_g_parse_stack_depth++
362677cb4d3eSLandon J. Fuller	_g_parse_stack[_g_parse_stack_depth] = _state
3627e83ce340SAdrian Chadd}
3628e83ce340SAdrian Chadd
362977cb4d3eSLandon J. Fuller# Fetch the current parser state
363077cb4d3eSLandon J. Fullerfunction parser_state_get() {
363177cb4d3eSLandon J. Fuller	if (_g_parse_stack_depth == 0)
363277cb4d3eSLandon J. Fuller		errorx("parser_state_get() called with empty parse stack")
363377cb4d3eSLandon J. Fuller
363477cb4d3eSLandon J. Fuller	return (_g_parse_stack[_g_parse_stack_depth])
3635e83ce340SAdrian Chadd}
363677cb4d3eSLandon J. Fuller
363777cb4d3eSLandon J. Fuller# Pop the current parser state
363877cb4d3eSLandon J. Fullerfunction parser_state_pop(_block_state, _closes_block) {
363977cb4d3eSLandon J. Fuller	if (_g_parse_stack_depth == 0)
364077cb4d3eSLandon J. Fuller		errorx("parser_state_pop() called with empty parse stack")
364177cb4d3eSLandon J. Fuller
364277cb4d3eSLandon J. Fuller	_closes_block = get(parser_state_get(), p_is_block)
364377cb4d3eSLandon J. Fuller
364477cb4d3eSLandon J. Fuller	delete _g_parse_stack[_g_parse_stack_depth]
364577cb4d3eSLandon J. Fuller	_g_parse_stack_depth--
364677cb4d3eSLandon J. Fuller
364777cb4d3eSLandon J. Fuller	if (_closes_block)
364877cb4d3eSLandon J. Fuller		debug("}")
364977cb4d3eSLandon J. Fuller}
365077cb4d3eSLandon J. Fuller
365177cb4d3eSLandon J. Fuller# Fetch the current context object associated with this parser state
365277cb4d3eSLandon J. Fuller# The object will be asserted as being an instance of the given class.
365377cb4d3eSLandon J. Fullerfunction parser_state_get_context(class, _ctx_obj) {
365477cb4d3eSLandon J. Fuller	_ctx_obj = get(parser_state_get(), p_ctx)
365577cb4d3eSLandon J. Fuller	obj_assert_class(_ctx_obj, class)
365677cb4d3eSLandon J. Fuller
365777cb4d3eSLandon J. Fuller	return (_ctx_obj)
365877cb4d3eSLandon J. Fuller}
365977cb4d3eSLandon J. Fuller
366077cb4d3eSLandon J. Fuller# Walk the parser state stack until a context object of the given class
366177cb4d3eSLandon J. Fuller# is found. If the top of the stack is reached without finding a context object
366277cb4d3eSLandon J. Fuller# of the requested type, an error will be thrown.
366377cb4d3eSLandon J. Fullerfunction parser_state_find_context(class, _state, _ctx, _i) {
366477cb4d3eSLandon J. Fuller	if (class == null)
366577cb4d3eSLandon J. Fuller		errorx("parser_state_find_context() called with null class")
366677cb4d3eSLandon J. Fuller
366777cb4d3eSLandon J. Fuller	# Find the first context instance inheriting from `class`
366877cb4d3eSLandon J. Fuller	for (_i = 0; _i < _g_parse_stack_depth; _i++) {
366977cb4d3eSLandon J. Fuller		_state = _g_parse_stack[_g_parse_stack_depth - _i]
367077cb4d3eSLandon J. Fuller		_ctx = get(_state, p_ctx)
367177cb4d3eSLandon J. Fuller
367277cb4d3eSLandon J. Fuller		# Check for match
367377cb4d3eSLandon J. Fuller		if (obj_is_instanceof(_ctx, class))
367477cb4d3eSLandon J. Fuller			return (_ctx)
367577cb4d3eSLandon J. Fuller	}
367677cb4d3eSLandon J. Fuller
367777cb4d3eSLandon J. Fuller	# Not found
367877cb4d3eSLandon J. Fuller	errorx("no context instance of type '" class_get_name(class) "' " \
367977cb4d3eSLandon J. Fuller	    "found in parse stack")
3680e83ce340SAdrian Chadd}
3681e83ce340SAdrian Chadd
3682e83ce340SAdrian Chadd#
3683e83ce340SAdrian Chadd# Find opening brace and push a new parser state for a brace-delimited block.
3684e83ce340SAdrian Chadd#
368577cb4d3eSLandon J. Fullerfunction parser_state_open_block(ctx) {
3686e83ce340SAdrian Chadd	if ($0 ~ "{" || getline_matching("^[ \t]*{") > 0) {
368777cb4d3eSLandon J. Fuller		parser_state_push(ctx, 1)
368877cb4d3eSLandon J. Fuller		sub("^[^{]*{", "", $0)
3689e83ce340SAdrian Chadd		return
3690e83ce340SAdrian Chadd	}
3691e83ce340SAdrian Chadd
369277cb4d3eSLandon J. Fuller	error("found '"$1 "' instead of expected '{'")
3693e83ce340SAdrian Chadd}
3694e83ce340SAdrian Chadd
3695e83ce340SAdrian Chadd#
3696e83ce340SAdrian Chadd# Find closing brace and pop parser states until the first
3697e83ce340SAdrian Chadd# brace-delimited block is discarded.
3698e83ce340SAdrian Chadd#
369977cb4d3eSLandon J. Fullerfunction parser_state_close_block(_next_state, _found_block) {
3700e83ce340SAdrian Chadd	if ($0 !~ "}")
3701e83ce340SAdrian Chadd		error("internal error - no closing brace")
3702e83ce340SAdrian Chadd
3703e83ce340SAdrian Chadd	# pop states until we exit the first enclosing block
3704e83ce340SAdrian Chadd	do {
370577cb4d3eSLandon J. Fuller		_next_state = parser_state_get()
370677cb4d3eSLandon J. Fuller		_found_block = get(_next_state, p_is_block)
370777cb4d3eSLandon J. Fuller		parser_state_pop()
370877cb4d3eSLandon J. Fuller	} while (!_found_block)
3709e83ce340SAdrian Chadd
3710e83ce340SAdrian Chadd	# strip everything prior to the block closure
3711e83ce340SAdrian Chadd	sub("^[^}]*}", "", $0)
3712e83ce340SAdrian Chadd}
3713e83ce340SAdrian Chadd
371477cb4d3eSLandon J. Fuller# Evaluates to true if the current parser state is defined with a context of
371577cb4d3eSLandon J. Fuller# the given class
371677cb4d3eSLandon J. Fullerfunction in_parser_context(class, _ctx) {
371777cb4d3eSLandon J. Fuller	if (class == null)
371877cb4d3eSLandon J. Fuller		errorx("called in_parser_context() with null class")
3719e83ce340SAdrian Chadd
372077cb4d3eSLandon J. Fuller	_ctx = get(parser_state_get(), p_ctx)
372177cb4d3eSLandon J. Fuller	return (obj_is_instanceof(_ctx, class))
3722e83ce340SAdrian Chadd}
3723e83ce340SAdrian Chadd
3724e83ce340SAdrian Chadd#
372577cb4d3eSLandon J. Fuller# Parse and return a revision range from the current line.
3726e83ce340SAdrian Chadd#
372777cb4d3eSLandon J. Fuller# 4
372877cb4d3eSLandon J. Fuller# 4-10	# revisions 4-10, inclusive
372977cb4d3eSLandon J. Fuller# > 4
373077cb4d3eSLandon J. Fuller# < 4
373177cb4d3eSLandon J. Fuller# >= 4
373277cb4d3eSLandon J. Fuller# <= 4
3733e83ce340SAdrian Chadd#
373477cb4d3eSLandon J. Fullerfunction parse_revrange(_start, _end, _robj) {
373577cb4d3eSLandon J. Fuller	_start = 0
373677cb4d3eSLandon J. Fuller	_end = 0
3737e83ce340SAdrian Chadd
373877cb4d3eSLandon J. Fuller	if ($2 ~ "[0-9]*-[0-9*]") {
373977cb4d3eSLandon J. Fuller		split($2, _g_rev_range, "[ \t]*-[ \t]*")
374077cb4d3eSLandon J. Fuller		_start = int(_g_rev_range[1])
374177cb4d3eSLandon J. Fuller		_end = int(_g_rev_range[2])
374277cb4d3eSLandon J. Fuller	} else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") {
374377cb4d3eSLandon J. Fuller		if ($2 == ">") {
374477cb4d3eSLandon J. Fuller			_start = int($3)+1
374577cb4d3eSLandon J. Fuller			_end = REV_MAX
374677cb4d3eSLandon J. Fuller		} else if ($2 == ">=") {
374777cb4d3eSLandon J. Fuller			_start = int($3)
374877cb4d3eSLandon J. Fuller			_end = REV_MAX
374977cb4d3eSLandon J. Fuller		} else if ($2 == "<" && int($3) > 0) {
375077cb4d3eSLandon J. Fuller			_start = 0
375177cb4d3eSLandon J. Fuller			_end = int($3)-1
375277cb4d3eSLandon J. Fuller		} else if ($2 == "<=") {
375377cb4d3eSLandon J. Fuller			_start = 0
375477cb4d3eSLandon J. Fuller			_end = int($3)-1
3755e83ce340SAdrian Chadd		} else {
375677cb4d3eSLandon J. Fuller			error("invalid revision descriptor")
3757e83ce340SAdrian Chadd		}
375877cb4d3eSLandon J. Fuller	} else if ($2 ~ "[1-9][0-9]*") {
375977cb4d3eSLandon J. Fuller		_start = int($2)
376077cb4d3eSLandon J. Fuller		_end = int($2)
376177cb4d3eSLandon J. Fuller	} else {
376277cb4d3eSLandon J. Fuller		error("invalid revision descriptor")
376377cb4d3eSLandon J. Fuller	}
376477cb4d3eSLandon J. Fuller
376577cb4d3eSLandon J. Fuller	return (revrange_new(_start, _end))
3766e83ce340SAdrian Chadd}
3767e83ce340SAdrian Chadd
3768e83ce340SAdrian Chadd#
376977cb4d3eSLandon J. Fuller# Parse a variable group block starting at the current line
3770e83ce340SAdrian Chadd#
377177cb4d3eSLandon J. Fuller# group "Group Name" {
377277cb4d3eSLandon J. Fuller# 	u8	var_name[10] {
377377cb4d3eSLandon J. Fuller#		...
377477cb4d3eSLandon J. Fuller#	}
377577cb4d3eSLandon J. Fuller#	...
377677cb4d3eSLandon J. Fuller# }
377777cb4d3eSLandon J. Fuller#
377877cb4d3eSLandon J. Fullerfunction parse_variable_group(_ctx, _groups, _group, _group_name) {
377977cb4d3eSLandon J. Fuller	_ctx = parser_state_get_context(NVRAM)
378077cb4d3eSLandon J. Fuller
378177cb4d3eSLandon J. Fuller	# Seek to the start of the name string
378277cb4d3eSLandon J. Fuller	shiftf(1)
378377cb4d3eSLandon J. Fuller
378477cb4d3eSLandon J. Fuller	# Parse the first line
378577cb4d3eSLandon J. Fuller	_group_name = stringconstant_parse_line($0)
378677cb4d3eSLandon J. Fuller
378777cb4d3eSLandon J. Fuller	# Incrementally parse line continuations
378877cb4d3eSLandon J. Fuller	while (get(_group_name, p_continued)) {
378977cb4d3eSLandon J. Fuller		getline
379077cb4d3eSLandon J. Fuller		stringconstant_append_line(_group_name, $0)
379177cb4d3eSLandon J. Fuller	}
379277cb4d3eSLandon J. Fuller
379377cb4d3eSLandon J. Fuller	debug("group \"" get(_group_name, p_value) "\" {")
379477cb4d3eSLandon J. Fuller
379577cb4d3eSLandon J. Fuller	# Register the new variable group
379677cb4d3eSLandon J. Fuller	_groups = get(_ctx, p_var_groups)
379777cb4d3eSLandon J. Fuller	_group = var_group_new(_group_name)
379877cb4d3eSLandon J. Fuller	array_append(_groups, _group)
379977cb4d3eSLandon J. Fuller
380077cb4d3eSLandon J. Fuller	# Push our variable group block
380177cb4d3eSLandon J. Fuller	parser_state_open_block(_group)
380277cb4d3eSLandon J. Fuller}
380377cb4d3eSLandon J. Fuller
380477cb4d3eSLandon J. Fuller
380577cb4d3eSLandon J. Fuller#
380677cb4d3eSLandon J. Fuller# Parse a variable definition block starting at the current line
380777cb4d3eSLandon J. Fuller#
380877cb4d3eSLandon J. Fuller# u8	var_name[10] {
380977cb4d3eSLandon J. Fuller#	all1	ignore
381077cb4d3eSLandon J. Fuller#	desc	...
381177cb4d3eSLandon J. Fuller# }
381277cb4d3eSLandon J. Fuller#
381377cb4d3eSLandon J. Fullerfunction parse_variable_defn(_ctx, _vaccess, _type, _name, _fmt, _var,
381477cb4d3eSLandon J. Fuller    _var_list)
3815e83ce340SAdrian Chadd{
381677cb4d3eSLandon J. Fuller	_ctx = parser_state_get_context(SymbolContext)
3817e83ce340SAdrian Chadd
381877cb4d3eSLandon J. Fuller	# Check for access modifier
381977cb4d3eSLandon J. Fuller	if ($1 == "private") {
382077cb4d3eSLandon J. Fuller		_vaccess = VAccessPrivate
382177cb4d3eSLandon J. Fuller		shiftf(1)
382277cb4d3eSLandon J. Fuller	} else if ($1 == "internal") {
382377cb4d3eSLandon J. Fuller		_vaccess = VAccessInternal
3824e83ce340SAdrian Chadd		shiftf(1)
3825e83ce340SAdrian Chadd	} else {
382677cb4d3eSLandon J. Fuller		_vaccess = VAccessPublic
3827e83ce340SAdrian Chadd	}
3828e83ce340SAdrian Chadd
382977cb4d3eSLandon J. Fuller	# Find the base type
383077cb4d3eSLandon J. Fuller	if ((_type = type_named($1)) == null)
383177cb4d3eSLandon J. Fuller		error("unknown type '" $1 "'")
3832e83ce340SAdrian Chadd
383377cb4d3eSLandon J. Fuller	# Parse (and trim) any array specifier from the variable name
383477cb4d3eSLandon J. Fuller	_name = $2
383577cb4d3eSLandon J. Fuller	_type = parse_array_type_specifier(_name, _type)
383677cb4d3eSLandon J. Fuller	sub(ARRAY_REGEX"$", "", _name)
383777cb4d3eSLandon J. Fuller
383877cb4d3eSLandon J. Fuller	# Look for an existing variable definition
383977cb4d3eSLandon J. Fuller	if (_name in _g_var_names) {
384077cb4d3eSLandon J. Fuller		error("variable identifier '" _name "' previously defined at " \
384177cb4d3eSLandon J. Fuller		    "line " get(_g_var_names[_name], p_line))
384277cb4d3eSLandon J. Fuller	}
384377cb4d3eSLandon J. Fuller
384477cb4d3eSLandon J. Fuller	# Construct new variable instance
384577cb4d3eSLandon J. Fuller	_var = var_new(_vaccess, _name, _type)
384677cb4d3eSLandon J. Fuller	debug((_private ? "private " : "") type_to_string(_type) " " _name " {")
384777cb4d3eSLandon J. Fuller
384877cb4d3eSLandon J. Fuller	# Register in global name table
384977cb4d3eSLandon J. Fuller	_g_var_names[_name] = _var
385077cb4d3eSLandon J. Fuller
385177cb4d3eSLandon J. Fuller	# Add to our parent context
385277cb4d3eSLandon J. Fuller	_var_list = get(_ctx, p_vars)
385377cb4d3eSLandon J. Fuller	array_append(_var_list, _var)
385477cb4d3eSLandon J. Fuller
385577cb4d3eSLandon J. Fuller	# Push our variable definition block
385677cb4d3eSLandon J. Fuller	parser_state_open_block(_var)
385777cb4d3eSLandon J. Fuller}
385877cb4d3eSLandon J. Fuller
385977cb4d3eSLandon J. Fuller
386077cb4d3eSLandon J. Fuller#
386177cb4d3eSLandon J. Fuller# Return a string containing the human-readable list of valid Fmt names
386277cb4d3eSLandon J. Fuller#
386377cb4d3eSLandon J. Fullerfunction fmt_get_human_readable_list(_result, _fmts, _fmt, _nfmts, _i)
386477cb4d3eSLandon J. Fuller{
386577cb4d3eSLandon J. Fuller	# Build up a string listing the valid formats
386677cb4d3eSLandon J. Fuller	_fmts = map_to_array(ValueFormats)
386777cb4d3eSLandon J. Fuller	_result = ""
386877cb4d3eSLandon J. Fuller
386977cb4d3eSLandon J. Fuller	_nfmts = array_size(_fmts)
387077cb4d3eSLandon J. Fuller	for (_i = 0; _i < _nfmts; _i++) {
387177cb4d3eSLandon J. Fuller		_fmt = array_get(_fmts, _i)
387277cb4d3eSLandon J. Fuller		if (_i+1 == _nfmts)
387377cb4d3eSLandon J. Fuller			_result = _result "or "
387477cb4d3eSLandon J. Fuller
387577cb4d3eSLandon J. Fuller		_result = _name_str \
387677cb4d3eSLandon J. Fuller		    "'" get(_fmt, p_name) "'"
387777cb4d3eSLandon J. Fuller
387877cb4d3eSLandon J. Fuller		if (_i+1 < _nfmts)
387977cb4d3eSLandon J. Fuller			_result = _result ", "
388077cb4d3eSLandon J. Fuller	}
388177cb4d3eSLandon J. Fuller
388277cb4d3eSLandon J. Fuller	obj_delete(_fmts)
388377cb4d3eSLandon J. Fuller	return (_result)
388477cb4d3eSLandon J. Fuller}
388577cb4d3eSLandon J. Fuller
388677cb4d3eSLandon J. Fuller#
388777cb4d3eSLandon J. Fuller# Parse a variable parameter from the current line
388877cb4d3eSLandon J. Fuller#
388977cb4d3eSLandon J. Fuller# fmt	(decimal|hex|macaddr|...)
389077cb4d3eSLandon J. Fuller# all1	ignore
389177cb4d3eSLandon J. Fuller# desc	"quoted string"
389277cb4d3eSLandon J. Fuller# help	"quoted string"
389377cb4d3eSLandon J. Fuller#
389477cb4d3eSLandon J. Fullerfunction parse_variable_param(param_name, _var, _vprops, _prop_id, _pval) {
389577cb4d3eSLandon J. Fuller	_var = parser_state_get_context(Var)
389677cb4d3eSLandon J. Fuller
389777cb4d3eSLandon J. Fuller	if (param_name == "fmt") {
389877cb4d3eSLandon J. Fuller		debug($1 " " $2)
389977cb4d3eSLandon J. Fuller
390077cb4d3eSLandon J. Fuller		# Check for an existing definition
390177cb4d3eSLandon J. Fuller		if ((_pval = get(_var, p_fmt)) != null) {
390277cb4d3eSLandon J. Fuller			error("fmt previously specified on line " \
390377cb4d3eSLandon J. Fuller			    obj_get_prop_nr(_var, p_fmt))
390477cb4d3eSLandon J. Fuller		}
390577cb4d3eSLandon J. Fuller
390677cb4d3eSLandon J. Fuller		# Validate arguments
390777cb4d3eSLandon J. Fuller		if (NF != 2) {
390877cb4d3eSLandon J. Fuller			error("'" $1 "' requires a single parameter value of " \
390977cb4d3eSLandon J. Fuller			    fmt_get_human_readable_list())
391077cb4d3eSLandon J. Fuller		}
391177cb4d3eSLandon J. Fuller
391277cb4d3eSLandon J. Fuller		if ((_pval = fmt_named($2)) == null) {
391377cb4d3eSLandon J. Fuller			error("'" $1 "' value '" $2 "' unrecognized. Must be " \
391477cb4d3eSLandon J. Fuller			    "one of " fmt_get_human_readable_list())
391577cb4d3eSLandon J. Fuller		}
391677cb4d3eSLandon J. Fuller
391777cb4d3eSLandon J. Fuller		# Set fmt reference
391877cb4d3eSLandon J. Fuller		set(_var, p_fmt, _pval)
391977cb4d3eSLandon J. Fuller	} else if (param_name == "all1") {
392077cb4d3eSLandon J. Fuller		debug($1 " " $2)
392177cb4d3eSLandon J. Fuller
392277cb4d3eSLandon J. Fuller		# Check for an existing definition
392377cb4d3eSLandon J. Fuller		if ((_pval = get(_var, p_ignall1)) != null) {
392477cb4d3eSLandon J. Fuller			error("all1 previously specified on line " \
392577cb4d3eSLandon J. Fuller			    obj_get_prop_nr(_var, p_ignall1))
392677cb4d3eSLandon J. Fuller		}
392777cb4d3eSLandon J. Fuller
392877cb4d3eSLandon J. Fuller		# Check argument
392977cb4d3eSLandon J. Fuller		if (NF != 2)
393077cb4d3eSLandon J. Fuller			error("'" $1 "'requires a single 'ignore' argument")
393177cb4d3eSLandon J. Fuller		else if ($2 != "ignore")
393277cb4d3eSLandon J. Fuller			error("unknown "$1" value '"$2"', expected 'ignore'")
393377cb4d3eSLandon J. Fuller
393477cb4d3eSLandon J. Fuller		# Set variable property
393577cb4d3eSLandon J. Fuller		set(_var, p_ignall1, 1)
393677cb4d3eSLandon J. Fuller	} else if (param_name == "desc" || param_name == "help") {
393777cb4d3eSLandon J. Fuller		# Fetch an indirect property reference for either the 'desc'
393877cb4d3eSLandon J. Fuller		# or 'help' property
393977cb4d3eSLandon J. Fuller		_prop_id = obj_get_named_prop_id(_var, param_name)
394077cb4d3eSLandon J. Fuller
394177cb4d3eSLandon J. Fuller		# Check for an existing definition
394277cb4d3eSLandon J. Fuller		if ((_pval = prop_get(_var, _prop_id)) != null) {
394377cb4d3eSLandon J. Fuller			error(get(_var, p_name) " '" $1 "' redefined " \
394477cb4d3eSLandon J. Fuller			    "(previously defined on line " \
394577cb4d3eSLandon J. Fuller			    obj_get_prop_id_nr(_var, _prop_id) ")")
394677cb4d3eSLandon J. Fuller		}
394777cb4d3eSLandon J. Fuller
394877cb4d3eSLandon J. Fuller		# Seek to the start of the desc/help string
394977cb4d3eSLandon J. Fuller		shiftf(1)
395077cb4d3eSLandon J. Fuller
395177cb4d3eSLandon J. Fuller		# Parse the first line
395277cb4d3eSLandon J. Fuller		_pval = stringconstant_parse_line($0)
395377cb4d3eSLandon J. Fuller
395477cb4d3eSLandon J. Fuller		# Incrementally parse line continuations
395577cb4d3eSLandon J. Fuller		while (get(_pval, p_continued)) {
395677cb4d3eSLandon J. Fuller			getline
395777cb4d3eSLandon J. Fuller			stringconstant_append_line(_pval, $0)
395877cb4d3eSLandon J. Fuller		}
395977cb4d3eSLandon J. Fuller
396077cb4d3eSLandon J. Fuller		debug(param_name " \"" get(_pval, p_value) "\"")
396177cb4d3eSLandon J. Fuller
396277cb4d3eSLandon J. Fuller		# Add to the var object
396377cb4d3eSLandon J. Fuller		prop_set(_var, _prop_id, _pval)
3964e83ce340SAdrian Chadd	} else {
396577cb4d3eSLandon J. Fuller		error("unknown variable property type: '" param_name "'")
3966e83ce340SAdrian Chadd	}
396777cb4d3eSLandon J. Fuller}
3968e83ce340SAdrian Chadd
396977cb4d3eSLandon J. Fuller
397077cb4d3eSLandon J. Fuller#
397177cb4d3eSLandon J. Fuller# Parse a top-level SROM layout block starting at the current line
397277cb4d3eSLandon J. Fuller#
397377cb4d3eSLandon J. Fuller# srom 4-7 {
397477cb4d3eSLandon J. Fuller#     0x000: ...
397577cb4d3eSLandon J. Fuller# }
397677cb4d3eSLandon J. Fuller#
397777cb4d3eSLandon J. Fullerfunction parse_srom_layout(_nvram, _srom_layouts, _revs, _layout) {
397877cb4d3eSLandon J. Fuller	_nvram = parser_state_get_context(NVRAM)
397977cb4d3eSLandon J. Fuller	_srom_layouts = get(_nvram, p_srom_layouts)
398077cb4d3eSLandon J. Fuller
398177cb4d3eSLandon J. Fuller	# Parse revision descriptor and register SROM
398277cb4d3eSLandon J. Fuller	# instance
398377cb4d3eSLandon J. Fuller	_revs = parse_revrange()
398477cb4d3eSLandon J. Fuller	_layout = srom_layout_new(_revs)
398577cb4d3eSLandon J. Fuller	nvram_add_srom_layout(_nvram, _layout)
398677cb4d3eSLandon J. Fuller
398777cb4d3eSLandon J. Fuller	debug("srom " revrange_to_string(_revs) " {")
398877cb4d3eSLandon J. Fuller
398977cb4d3eSLandon J. Fuller	# Push new SROM parser state
399077cb4d3eSLandon J. Fuller	parser_state_open_block(_layout)
399177cb4d3eSLandon J. Fuller}
399277cb4d3eSLandon J. Fuller
399377cb4d3eSLandon J. Fuller
399477cb4d3eSLandon J. Fuller#
399577cb4d3eSLandon J. Fuller# Parse a nested srom range filter block starting at the current line
399677cb4d3eSLandon J. Fuller# srom 4-7 {
399777cb4d3eSLandon J. Fuller#	# Filter block
399877cb4d3eSLandon J. Fuller# 	srom 5 {
399977cb4d3eSLandon J. Fuller#		0x000: ...
400077cb4d3eSLandon J. Fuller#	}
400177cb4d3eSLandon J. Fuller# }
400277cb4d3eSLandon J. Fuller#
400377cb4d3eSLandon J. Fullerfunction parse_srom_layout_filter(_parent, _revs, _filter) {
400477cb4d3eSLandon J. Fuller	_parent = parser_state_get_context(SromLayout)
400577cb4d3eSLandon J. Fuller
400677cb4d3eSLandon J. Fuller	# Parse revision descriptor
400777cb4d3eSLandon J. Fuller	_revs = parse_revrange()
400877cb4d3eSLandon J. Fuller
400977cb4d3eSLandon J. Fuller	# Construct the filter (which also validates the revision range)
401077cb4d3eSLandon J. Fuller	_filter = srom_layout_filter_new(_parent, _revs)
401177cb4d3eSLandon J. Fuller
401277cb4d3eSLandon J. Fuller	debug("srom " revrange_to_string(_revs) " {")
401377cb4d3eSLandon J. Fuller
401477cb4d3eSLandon J. Fuller	# Push new SROM parser state
401577cb4d3eSLandon J. Fuller	parser_state_open_block(_filter)
401677cb4d3eSLandon J. Fuller}
401777cb4d3eSLandon J. Fuller
401877cb4d3eSLandon J. Fuller
401977cb4d3eSLandon J. Fuller#
402077cb4d3eSLandon J. Fuller# Parse a SROM offset segment's attribute list from the current line
402177cb4d3eSLandon J. Fuller#
402277cb4d3eSLandon J. Fuller# <empty line>
402377cb4d3eSLandon J. Fuller# (&0xF0, >>4, =0x5340)
402477cb4d3eSLandon J. Fuller# ()
402577cb4d3eSLandon J. Fuller#
402677cb4d3eSLandon J. Fuller# Attribute designators:
402777cb4d3eSLandon J. Fuller#	&0xF	Mask value with 0xF
402877cb4d3eSLandon J. Fuller#	<<4	Shift left 4 bits
402977cb4d3eSLandon J. Fuller#	>>4	Shift right 4 bits
403077cb4d3eSLandon J. Fuller#	=0x53	The parsed value must be equal to this constant value
403177cb4d3eSLandon J. Fuller#
403277cb4d3eSLandon J. Fuller# May be followed by a | indicating that this segment should be OR'd with the
403377cb4d3eSLandon J. Fuller# segment that follows, or a terminating , indicating that a new offset's
403477cb4d3eSLandon J. Fuller# list of segments may follow.
403577cb4d3eSLandon J. Fuller#
403677cb4d3eSLandon J. Fullerfunction parse_srom_segment_attributes(offset, type, _attrs, _num_attr, _attr,
403777cb4d3eSLandon J. Fuller    _mask, _shift, _value, _i)
403877cb4d3eSLandon J. Fuller{
403977cb4d3eSLandon J. Fuller	# seek to offset (attributes...) or end of the offset expr (|,)
4040e83ce340SAdrian Chadd	sub("^[^,(|){}]+", "", $0)
4041e83ce340SAdrian Chadd
404277cb4d3eSLandon J. Fuller	# defaults
404377cb4d3eSLandon J. Fuller	_mask = type_get_default_mask(type)
404477cb4d3eSLandon J. Fuller	_shift = 0
4045e83ce340SAdrian Chadd
404677cb4d3eSLandon J. Fuller	# parse attributes
4047e83ce340SAdrian Chadd	if ($1 ~ "^\\(") {
4048e83ce340SAdrian Chadd		# extract attribute list
404977cb4d3eSLandon J. Fuller		if (match($0, /\([^|\(\)]*\)/) <= 0)
4050e83ce340SAdrian Chadd			error("expected attribute list")
4051e83ce340SAdrian Chadd
405277cb4d3eSLandon J. Fuller		_attrs = substr($0, RSTART+1, RLENGTH-2)
405377cb4d3eSLandon J. Fuller
405477cb4d3eSLandon J. Fuller		# drop attribute list from the input line
4055e83ce340SAdrian Chadd		$0 = substr($0, RSTART+RLENGTH, length($0) - RSTART+RLENGTH)
4056e83ce340SAdrian Chadd
4057e83ce340SAdrian Chadd		# parse attributes
405877cb4d3eSLandon J. Fuller		_num_attr = split(_attrs, _g_attrs, ",[ \t]*")
405977cb4d3eSLandon J. Fuller		for (_i = 1; _i <= _num_attr; _i++) {
406077cb4d3eSLandon J. Fuller			_attr = _g_attrs[_i]
406177cb4d3eSLandon J. Fuller
406277cb4d3eSLandon J. Fuller			if (sub("^&[ \t]*", "", _attr) > 0) {
406377cb4d3eSLandon J. Fuller				_mask = parse_uint_string(_attr)
406477cb4d3eSLandon J. Fuller			} else if (sub("^<<[ \t]*", "", _attr) > 0) {
406577cb4d3eSLandon J. Fuller				_shift = - parse_uint_string(_attr)
406677cb4d3eSLandon J. Fuller			} else if (sub("^>>[ \t]*", "", _attr) > 0) {
406777cb4d3eSLandon J. Fuller				_shift = parse_uint_string(_attr)
406877cb4d3eSLandon J. Fuller			} else if (sub("^=[ \t]*", "", _attr) > 0) {
406977cb4d3eSLandon J. Fuller				_value = _attr
4070e83ce340SAdrian Chadd			} else {
407177cb4d3eSLandon J. Fuller				error("unknown attribute '" _attr "'")
4072e83ce340SAdrian Chadd			}
4073e83ce340SAdrian Chadd		}
4074e83ce340SAdrian Chadd	}
4075e83ce340SAdrian Chadd
407677cb4d3eSLandon J. Fuller	return (srom_segment_new(offset, type, _mask, _shift, _value))
4077e83ce340SAdrian Chadd}
4078e83ce340SAdrian Chadd
407977cb4d3eSLandon J. Fuller#
408077cb4d3eSLandon J. Fuller# Parse a SROM offset's segment declaration from the current line
408177cb4d3eSLandon J. Fuller#
408277cb4d3eSLandon J. Fuller# +0x0:	u8 (&0xF0, >>4)		# read 8 bits at +0x0 (relative to srom entry
408377cb4d3eSLandon J. Fuller#				# offset, apply 0xF0 mask, shift >> 4
408477cb4d3eSLandon J. Fuller# 0x10:	u8 (&0xF0, >>4)		# identical to above, but perform the read at
408577cb4d3eSLandon J. Fuller#				# absolute offset 0x10
408677cb4d3eSLandon J. Fuller#
408777cb4d3eSLandon J. Fuller# +0x0: u8			# no attributes
408877cb4d3eSLandon J. Fuller# 0x10: u8
408977cb4d3eSLandon J. Fuller#
409077cb4d3eSLandon J. Fuller# +0x0				# simplified forms denoted by lack of ':'; the
409177cb4d3eSLandon J. Fuller# 0x0				# type is inherited from the parent SromEntry
409277cb4d3eSLandon J. Fuller#
409377cb4d3eSLandon J. Fuller#
409477cb4d3eSLandon J. Fullerfunction parse_srom_segment(base_offset, base_type, _simple, _type, _type_str,
409577cb4d3eSLandon J. Fuller    _offset, _attrs, _num_attr, _attr, _mask, _shift, _off_desc)
409677cb4d3eSLandon J. Fuller{
409777cb4d3eSLandon J. Fuller	# Fetch the offset value
409877cb4d3eSLandon J. Fuller	_offset = $1
4099e83ce340SAdrian Chadd
410077cb4d3eSLandon J. Fuller	# Offset string must be one of:
410177cb4d3eSLandon J. Fuller	# 	simplified entry: <offset|+reloff>
410277cb4d3eSLandon J. Fuller	#		Provides only the offset, with the type inherited
410377cb4d3eSLandon J. Fuller	#		from the original variable definition
410477cb4d3eSLandon J. Fuller	#	standard entry: <offset|+reloff>:
410577cb4d3eSLandon J. Fuller	#		Provides the offset, followed by a type
410677cb4d3eSLandon J. Fuller	#
410777cb4d3eSLandon J. Fuller	# We differentiate the two by looking for (and simultaneously removing)
410877cb4d3eSLandon J. Fuller	# the trailing ':'
410977cb4d3eSLandon J. Fuller	if (!sub(/:$/, "", _offset))
411077cb4d3eSLandon J. Fuller		_simple = 1
4111e83ce340SAdrian Chadd
411277cb4d3eSLandon J. Fuller	# The offset may either be absolute (e.g. 0x180) or relative (e.g.
411377cb4d3eSLandon J. Fuller	# +0x01).
411477cb4d3eSLandon J. Fuller	#
411577cb4d3eSLandon J. Fuller	# If we find a relative offset definition, we must trim the leading '+'
411677cb4d3eSLandon J. Fuller	# and then add the base offset
411777cb4d3eSLandon J. Fuller	if (sub(/^\+/, "", _offset)) {
411877cb4d3eSLandon J. Fuller		_offset = base_offset + parse_uint_offset(_offset)
411977cb4d3eSLandon J. Fuller	} else {
412077cb4d3eSLandon J. Fuller
412177cb4d3eSLandon J. Fuller		_offset = parse_uint_offset(_offset)
412277cb4d3eSLandon J. Fuller	}
412377cb4d3eSLandon J. Fuller
412477cb4d3eSLandon J. Fuller	# If simplified form, use the base type of the SROM entry. Otherwise,
412577cb4d3eSLandon J. Fuller	# we need to parse the type.
412677cb4d3eSLandon J. Fuller	if (_simple) {
412777cb4d3eSLandon J. Fuller		_type = base_type
412877cb4d3eSLandon J. Fuller	} else {
412977cb4d3eSLandon J. Fuller		_type_str = $2
413077cb4d3eSLandon J. Fuller		sub(/,$/, "", _type_str) # trim trailing ',', if any
413177cb4d3eSLandon J. Fuller
413277cb4d3eSLandon J. Fuller		if ((_type = parse_type_string(_type_str)) == null)
413377cb4d3eSLandon J. Fuller			error("unknown type '" _type_str "'")
413477cb4d3eSLandon J. Fuller	}
413577cb4d3eSLandon J. Fuller
413677cb4d3eSLandon J. Fuller	# Parse the trailing (... attributes ...), if any
413777cb4d3eSLandon J. Fuller	return (parse_srom_segment_attributes(_offset, _type))
413877cb4d3eSLandon J. Fuller}
413977cb4d3eSLandon J. Fuller
414077cb4d3eSLandon J. Fuller#
414177cb4d3eSLandon J. Fuller# Parse a SROM variable entry from the current line
414277cb4d3eSLandon J. Fuller# <offset>: <type> <varname><array spec> ...
414377cb4d3eSLandon J. Fuller#
414477cb4d3eSLandon J. Fullerfunction parse_srom_variable_entry(_srom, _srom_revs, _rev_start, _rev_end,
414577cb4d3eSLandon J. Fuller    _srom_entries, _srom_revmap, _prev_entry, _ctx, _base_offset, _name,
414677cb4d3eSLandon J. Fuller    _stype, _var, _entry, _offset, _seg, _i)
414777cb4d3eSLandon J. Fuller{
414877cb4d3eSLandon J. Fuller	# Fetch our parent context
414977cb4d3eSLandon J. Fuller	_ctx = parser_state_get_context(SromContext)
415077cb4d3eSLandon J. Fuller	_srom_revs = get(_ctx, p_revisions)
415177cb4d3eSLandon J. Fuller	_rev_start = get(_srom_revs, p_start)
415277cb4d3eSLandon J. Fuller	_rev_end = get(_srom_revs, p_end)
415377cb4d3eSLandon J. Fuller
415477cb4d3eSLandon J. Fuller	# Locate our enclosing layout
415577cb4d3eSLandon J. Fuller	_srom = parser_state_find_context(SromLayout)
415677cb4d3eSLandon J. Fuller	_srom_entries = get(_srom, p_entries)
415777cb4d3eSLandon J. Fuller	_srom_revmap = get(_srom, p_revmap)
415877cb4d3eSLandon J. Fuller
415977cb4d3eSLandon J. Fuller	# Verify argument count
416077cb4d3eSLandon J. Fuller	if (NF < 3) {
416177cb4d3eSLandon J. Fuller		error("unrecognized srom entry syntax; must specify at " \
416277cb4d3eSLandon J. Fuller		    "least \"<offset>: <type> <variable name>\"")
416377cb4d3eSLandon J. Fuller	}
416477cb4d3eSLandon J. Fuller
416577cb4d3eSLandon J. Fuller	# Parse the base offset
416677cb4d3eSLandon J. Fuller	_base_offset = parse_uint_offset($1)
416777cb4d3eSLandon J. Fuller
416877cb4d3eSLandon J. Fuller	# Parse the base type
416977cb4d3eSLandon J. Fuller	if ((_stype = type_named($2)) == null)
417077cb4d3eSLandon J. Fuller		error("unknown type '" $2 "'")
417177cb4d3eSLandon J. Fuller
417277cb4d3eSLandon J. Fuller	# Parse (and trim) any array specifier from the variable name
417377cb4d3eSLandon J. Fuller	_name = $3
417477cb4d3eSLandon J. Fuller	_stype = parse_array_type_specifier(_name, _stype)
417577cb4d3eSLandon J. Fuller	sub(ARRAY_REGEX"$", "", _name)
417677cb4d3eSLandon J. Fuller
417777cb4d3eSLandon J. Fuller	# Locate the variable definition
417877cb4d3eSLandon J. Fuller	if (!(_name in _g_var_names))
417977cb4d3eSLandon J. Fuller		error("no definition found for variable '" _name "'")
418077cb4d3eSLandon J. Fuller	_var = _g_var_names[_name]
418177cb4d3eSLandon J. Fuller
418277cb4d3eSLandon J. Fuller	# The SROM entry type must be a subtype of the variable's declared
418377cb4d3eSLandon J. Fuller	# type
418477cb4d3eSLandon J. Fuller	if (!type_can_represent(get(_var, p_type), _stype)) {
418577cb4d3eSLandon J. Fuller		error("'" type_to_string(_stype) "' SROM value cannot be " \
418677cb4d3eSLandon J. Fuller		    "coerced to '" type_to_string(get(_var, p_type)) " " _name \
418777cb4d3eSLandon J. Fuller		    "' variable")
418877cb4d3eSLandon J. Fuller	}
418977cb4d3eSLandon J. Fuller
419077cb4d3eSLandon J. Fuller	# Create and register our new offset entry
419177cb4d3eSLandon J. Fuller	_entry = srom_entry_new(_var, _srom_revs, _base_offset, _stype)
419277cb4d3eSLandon J. Fuller	srom_layout_add_entry(_srom, _entry)
419377cb4d3eSLandon J. Fuller
419477cb4d3eSLandon J. Fuller	# Seek to either the block start ('{'), or the attributes to be
419577cb4d3eSLandon J. Fuller	# used for a single offset/segment entry at `offset`
419677cb4d3eSLandon J. Fuller	shiftf(3)
419777cb4d3eSLandon J. Fuller
419877cb4d3eSLandon J. Fuller	# Using the block syntax? */
419977cb4d3eSLandon J. Fuller	if ($1 == "{") {
420077cb4d3eSLandon J. Fuller		debug(sprintf("0x%03x: %s %s {", _base_offset,
420177cb4d3eSLandon J. Fuller		    type_to_string(_stype), _name))
420277cb4d3eSLandon J. Fuller		parser_state_open_block(_entry)
420377cb4d3eSLandon J. Fuller	} else {
420477cb4d3eSLandon J. Fuller		# Otherwise, we're using the simplified syntax -- create and
420577cb4d3eSLandon J. Fuller		# register our implicit SromOffset
420677cb4d3eSLandon J. Fuller		_offset = srom_offset_new()
420777cb4d3eSLandon J. Fuller		array_append(get(_entry, p_offsets), _offset)
420877cb4d3eSLandon J. Fuller
420977cb4d3eSLandon J. Fuller		# Parse and register simplified segment syntax
421077cb4d3eSLandon J. Fuller		_seg = parse_srom_segment_attributes(_base_offset, _stype)
421177cb4d3eSLandon J. Fuller		array_append(get(_offset, p_segments), _seg)
421277cb4d3eSLandon J. Fuller
421377cb4d3eSLandon J. Fuller		debug(sprintf("0x%03x: %s %s { %s }", _base_offset,
421477cb4d3eSLandon J. Fuller		    type_to_string(_stype), _name, segment_to_string(_seg)))
421577cb4d3eSLandon J. Fuller	}
421677cb4d3eSLandon J. Fuller}
421777cb4d3eSLandon J. Fuller
421877cb4d3eSLandon J. Fuller#
421977cb4d3eSLandon J. Fuller# Parse all SromSegment entry segments readable starting at the current line
422077cb4d3eSLandon J. Fuller#
422177cb4d3eSLandon J. Fuller# <offset|+reloff>[,|]?
422277cb4d3eSLandon J. Fuller# <offset|+reloff>: <type>[,|]?
422377cb4d3eSLandon J. Fuller# <offset|+reloff>: <type> (<attributes>)[,|]?
422477cb4d3eSLandon J. Fuller#
422577cb4d3eSLandon J. Fullerfunction parse_srom_entry_segments(_entry, _base_off, _base_type, _offs,
422677cb4d3eSLandon J. Fuller    _offset, _segs, _seg, _more_seg, _more_vals)
422777cb4d3eSLandon J. Fuller{
422877cb4d3eSLandon J. Fuller	_entry = parser_state_get_context(SromEntry)
422977cb4d3eSLandon J. Fuller	_base_off = get(_entry, p_base_offset)
423077cb4d3eSLandon J. Fuller	_offs = get(_entry, p_offsets)
423177cb4d3eSLandon J. Fuller
423277cb4d3eSLandon J. Fuller	_base_type = get(_entry, p_type)
423377cb4d3eSLandon J. Fuller	_base_type = type_get_base(_base_type)
423477cb4d3eSLandon J. Fuller
423577cb4d3eSLandon J. Fuller	# Parse all offsets
4236e83ce340SAdrian Chadd	do {
423777cb4d3eSLandon J. Fuller		# Create a SromOffset
423877cb4d3eSLandon J. Fuller		_offset = srom_offset_new()
423977cb4d3eSLandon J. Fuller		_segs = get(_offset, p_segments)
4240e83ce340SAdrian Chadd
424177cb4d3eSLandon J. Fuller		array_append(_offs, _offset)
4242e83ce340SAdrian Chadd
424377cb4d3eSLandon J. Fuller		# Parse all segments
4244e83ce340SAdrian Chadd		do {
424577cb4d3eSLandon J. Fuller			_seg = parse_srom_segment(_base_off, _base_type)
424677cb4d3eSLandon J. Fuller			array_append(_segs, _seg)
424777cb4d3eSLandon J. Fuller
424877cb4d3eSLandon J. Fuller			# Do more segments follow?
4249e83ce340SAdrian Chadd			_more_seg = ($1 == "|")
4250e83ce340SAdrian Chadd			if (_more_seg)
4251e83ce340SAdrian Chadd				shiftf(1, 1)
425277cb4d3eSLandon J. Fuller
425377cb4d3eSLandon J. Fuller			if (_more_seg)
425477cb4d3eSLandon J. Fuller				debug(segment_to_string(_seg) " |")
425577cb4d3eSLandon J. Fuller			else
425677cb4d3eSLandon J. Fuller				debug(segment_to_string(_seg))
4257e83ce340SAdrian Chadd		} while (_more_seg)
425877cb4d3eSLandon J. Fuller
425977cb4d3eSLandon J. Fuller		# Do more offsets follow?
4260e83ce340SAdrian Chadd		_more_vals = ($1 == ",")
4261e83ce340SAdrian Chadd		if (_more_vals)
4262e83ce340SAdrian Chadd			shiftf(1, 1)
4263e83ce340SAdrian Chadd	} while (_more_vals)
4264e83ce340SAdrian Chadd}
4265