+/*----- Register types ----------------------------------------------------*/
+
+struct tvec_state; /* forward declaration */
+
+struct tvec_regty {
+ /* A register type. */
+
+ void (*init)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/);
+ /* Initialize the value in @*rv@. This will be called before any other
+ * function acting on the value, including @release@.
+ */
+
+ void (*release)(union tvec_regval */*rv*/,
+ const struct tvec_regdef */*rd*/);
+ /* Release any resources associated with the value in @*rv@. */
+
+ int (*eq)(const union tvec_regval */*rv0*/,
+ const union tvec_regval */*rv1*/,
+ const struct tvec_regdef */*rd*/);
+ /* Return nonzero if @*rv0@ and @*rv1@ are equal values. Asymmetric
+ * criteria are permitted: @tvec_checkregs@ calls @eq@ with the input
+ * register as @rv0@ and the output as @rv1@.
+ */
+
+ int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/,
+ const struct tvec_regdef */*rd*/);
+ /* Serialize the value @*rv@, writing the result to @b@. Return zero on
+ * success, or %$-1$% on error.
+ */
+
+ int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/,
+ const struct tvec_regdef */*rd*/);
+ /* Deserialize a value from @b@, storing it in @*rv@. Return zero on
+ * success, or %$-1$% on error.
+ */
+
+ int (*parse)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/,
+ struct tvec_state */*tv*/);
+ /* Parse a value from @tv->fp@, storing it in @*rv@. Return zero on
+ * success, or %$-1$% on error, having reported one or more errors via
+ * @tvec_error@ or @tvec_syntax@. A successful return should leave the
+ * input position at the start of the next line; the caller will flush
+ * the remainder of the line itself.
+ */
+
+ void (*dump)(const union tvec_regval */*rv*/,
+ const struct tvec_regdef */*rd*/,
+ unsigned /*style*/,
+ const struct gprintf_ops */*gops*/, void */*go*/);
+#define TVSF_COMPACT 1u
+ /* Write a human-readable representation of the value @*rv@ using
+ * @gprintf@ on @gops@ and @go@. The @style@ is a collection of flags:
+ * if @TVSF_COMPACT@ is set, then output should be minimal, and must fit
+ * on a single line; otherwise, output may consist of multiple lines and
+ * may contain redundant information if that is likely to be useful to a
+ * human reader.
+ */
+};
+
+/*----- Test descriptions -------------------------------------------------*/
+
+typedef void tvec_testfn(const struct tvec_reg */*in*/,
+ struct tvec_reg */*out*/,
+ void */*ctx*/);
+ /* A test function. It should read inputs from @in@ and write outputs to
+ * @out@. The @TVRF_LIVE@ is set on inputs which are actually present, and
+ * on outputs which are wanted to test. A test function can set additional
+ * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing
+ * @TVRF_LIVE@ on a wanted output causes a mismatch.
+ *
+ * A test function may be called zero or more times by the environment. In
+ * particular, it may be called multiple times, though usually by prior
+ * arrangement with the environment.
+ *
+ * The @ctx@ is supplied by the environment's @run@ function (see below).
+ * The default environment calls the test function once, with a null
+ * @ctx@. There is no expectation that the environment's context has
+ * anything to do with the test function's context.
+ */
+
+struct tvec_env;
+
+typedef int tvec_setvarfn(struct tvec_state */*tv*/, const char */*var*/,
+ const union tvec_regval */*rv*/, void */*ctx*/);
+ /* Called after a variable is read. Return zero on success or %$-1$% on
+ * error. This function is never called if the test group is skipped.
+ */
+
+struct tvec_vardef {
+ size_t regsz; /* (minimum) register size */
+ tvec_setvarfn *setvar; /* function to set variable */
+ struct tvec_regdef def; /* register definition */
+};
+
+typedef void tvec_envsetupfn(struct tvec_state */*tv*/,
+ const struct tvec_env */*env*/,
+ void */*pctx*/, void */*ctx*/);
+ /* Initialize the context; called at the start of a test group; @pctx@ is
+ * null for environments called by the core, but may be non-null for
+ * subordinate environments. If setup fails, the function should call
+ * @tvec_skipgroup@ with a suitable excuse. The @set@, @after@, and
+ * @teardown@ entry points will still be called, but @before@ and @run@
+ * will not.
+ */
+
+typedef const struct tvec_vardef *tvec_envfindvarfn
+ (struct tvec_state */*tv*/, const char */*name*/,
+ void **/*ctx_out*/, void */*ctx*/);
+ /* Called when the parser finds a %|@var|%' special variable. If a
+ * suitable variable was found, set @*ctx_out@ to a suitable context and
+ * return the variable definition; the context will be passed to the
+ * variable definition's @setvar@ function. If no suitable variable was
+ * found, then return null.
+ */
+
+typedef void tvec_envbeforefn(struct tvec_state */*tv*/, void */*ctx*/);
+ /* Called prior to running a test. This is the right place to act on any
+ * `%|@var|%' settings. If preparation fails, the function should call
+ * @tvec_skip@ with a suitable excuse. This function is never called if
+ * the test group is skipped. It %%\emph{is}%% called if the test will be
+ * skipped due to erroneous test data. It should check the @TVSF_ACTIVE@
+ * flag if necessary.
+ */
+
+typedef void tvec_envrunfn(struct tvec_state */*tv*/,
+ tvec_testfn */*fn*/, void */*ctx*/);
+ /* Run the test. It should either call @tvec_skip@, or run @fn@ one or
+ * more times. In the latter case, it is responsible for checking the
+ * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will
+ * check the register values against the supplied test vector, while
+ * @tvec_check@ does pretty much everything necessary. This function is
+ * never called if the test group is skipped.
+ */
+
+typedef void tvec_envafterfn(struct tvec_state */*tv*/, void */*ctx*/);
+ /* Called after running or skipping a test. Typical actions involve
+ * resetting whatever things were established by @set@. This function
+ * %%\emph{is}%% called if the test group is skipped or the test data is
+ * erroneous, so that the test environment can reset variables set by the
+ * @set@ entry point. It should check the @TVSF_SKIP@ flag if necessary.
+ */
+
+typedef void tvec_envteardownfn(struct tvec_state */*tv*/, void */*ctx*/);
+ /* Tear down the environment: called at the end of a test group. */
+
+
+struct tvec_env {
+ /* A test environment sets things up for and arranges to run the test.
+ *
+ * The caller is responsible for allocating storage for the environment's
+ * context, based on the @ctxsz@ slot, and freeing it later; this space is
+ * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@
+ * is zero then @ctx@ is null.
+ */
+
+ size_t ctxsz; /* environment context size */
+
+ tvec_envsetupfn *setup; /* setup for group */
+ tvec_envfindvarfn *findvar; /* find variable */
+ tvec_envbeforefn *before; /* prepare for test */
+ tvec_envrunfn *run; /* run test function */
+ tvec_envafterfn *after; /* clean up after test */
+ tvec_envteardownfn *teardown; /* tear down after group */
+};
+
+struct tvec_test {
+ /* A test description. */
+
+ const char *name; /* name of the test */
+ const struct tvec_regdef *regs; /* descriptions of the registers */
+ const struct tvec_env *env; /* environment to run test in */
+ tvec_testfn *fn; /* test function */
+};
+#define TVEC_ENDTESTS { 0, 0, 0, 0 }
+
+enum {
+ /* Register output dispositions. */
+
+ TVRD_INPUT, /* input-only register */
+ TVRD_OUTPUT, /* output-only (input is dead) */
+ TVRD_MATCH, /* matching (equal) registers */
+ TVRD_FOUND, /* mismatching output register */
+ TVRD_EXPECT, /* mismatching input register */
+ TVRD_LIMIT /* (number of dispositions) */
+};
+