/********************************************************************
* Description: blocks.c
*   Assorted simple functional blocks for HAL.
*
*   See the "Users Manual" at emc2/docs/Hal_Introduction.pdf
*
*   This HAL component contains an assortment of simple blocks.
*   Each individual type of block is invoked by a parameter on
*   the insmdo command line.  Each parameter is of the form:
*   <blockname>=<number_of_blocks>
*
*   For example, wcomp=2 installs two window comparators.
*
*   List of blocks currently implemented:
*     constant = pin controlled by parameter
*     wcomp = window comparator - out is true if min < in < max
*     comp = 2 input comparator - out is true if in1 > in0
*     sum2 = 2 input summer - out = in1 * gain1 + in2 * gain2
*     mult2 = 2 input multiplier: out = in0 * in1
*     mux2 = two input analog mux - out = in1 if sel is true, else in0
*     mux4 = four input analog mux - out = in<n> based on sel1,sel0
*     integ = integrator, out = integral of in
*     ddt = differentiator, out = derivative of in
*     limit1 = first order limiter (limits output)
*     limit2 = second order limiter (limits output and 1st derivative)
*     limit3 = third order limiter (limits output, 1st & 2nd derivative)
*     estop = latch for estops or other faults, with reset
*     not = logical inverter - out is true if in is false
*     and2 = 2 input logical and - out is true if in1 and in2 are true
*     or2 = 2 input logical or - out is true if in1 or in2 is true
*     scale = gain/offset block - out = in * gain + offset
*     lowpass = lowpass filter - out = last_out * (1 - gain) + in * gain
*     match8 = 8 bit binary match detector (with input for cascading)
*     hypot = like libm hypot(): out = sqrt(in0*in0 + in1*in1 + in2*in2)
*     minmax = min/max block: min and max outputs are peak values of in
*
*********************************************************************
*
* Author: John Kasunich (jmkasunich AT att DOT net)
* License: GPL Version 2
* Created on: 2004/06/14
* System: Linux
*
* Copyright (c) 2004 All rights reserved.
*
* Last change:
* $Revision$
* $Author$
* $Date$
*
********************************************************************/

#ifndef RTAPI
#error This is a realtime component only!
#endif

#include "rtapi.h"		/* RTAPI realtime OS API */
#include "rtapi_app.h"		/* RTAPI realtime module decls */
#include "hal.h"		/* HAL public API decls */

#include <linux/types.h>
#define hypot libm_hypot
#include "rtapi_math.h"
#undef hypot

/* module information */
MODULE_AUTHOR("John Kasunich");
MODULE_DESCRIPTION("Functional blocks for EMC HAL");
MODULE_LICENSE("GPL");
static int constant = 0;	/* number of constants */
RTAPI_MP_INT(constant, "constants");
static int wcomp = 0;		/* number of window comps */
RTAPI_MP_INT(wcomp, "window comparators");
static int comp = 0;		/* number of 2-input comps */
RTAPI_MP_INT(comp, "2-input comparators");
static int sum2 = 0;		/* number of 2-input summers */
RTAPI_MP_INT(sum2, "2-input summers");
static int mult2 = 0;		/* number of 2-input multipliers */
RTAPI_MP_INT(mult2, "2-input multiplier");
static int hypot = 0;		/* number of hypot calculators */
RTAPI_MP_INT(hypot, "hypot calculators");
static int mux2 = 0;		/* number of 2-input muxes */
RTAPI_MP_INT(mux2, "2-input multiplexors");
static int mux4 = 0;		/* number of 4-input muxes */
RTAPI_MP_INT(mux4, "4-input multiplexors");
static int integ = 0;		/* number of integerators */
RTAPI_MP_INT(integ, "integrators");
static int ddt = 0;		/* number of differentiators */
RTAPI_MP_INT(ddt, "differentiators");
static int limit1 = 0;		/* number of limiters */
RTAPI_MP_INT(limit1, "first order limiters");
static int limit2 = 0;		/* number of limiters */
RTAPI_MP_INT(limit2, "second order limiters");
static int limit3 = 0;		/* number of limiters */
RTAPI_MP_INT(limit3, "third order limiters");
static int n_estop = 0;		/* number of estop blocks */
RTAPI_MP_INT(n_estop, "estop latch blocks");
static int not = 0;            /* number of logical nots */
RTAPI_MP_INT(not, "logical inverters");
static int and2 = 0;            /* number of 2-input logical ands */
RTAPI_MP_INT(and2, "2-input logical ands");
static int or2 = 0;            /* number of 2-input logical ors */
RTAPI_MP_INT(or2, "2-input logical ors");
static int scale = 0;            /* number of scales */
RTAPI_MP_INT(scale, "scales");
static int lowpass = 0;            /* number of lowpass-filters */
RTAPI_MP_INT(lowpass, "lowpass-filters");
static int match8 = 0;            /* number of match detectors */
RTAPI_MP_INT(match8, "match detectors");
static int minmax = 0;            /* number of min/max blocks */
RTAPI_MP_INT(minmax, "minmax blocks");

/***********************************************************************
*                STRUCTURES AND GLOBAL VARIABLES                       *
************************************************************************/

/** These structures contain the runtime data for a single block. */

typedef struct {
    hal_float_t *out;		/* pin: output */
    hal_float_t value;		/* parameter: value of constant */
} constant_t;

typedef struct {
    hal_float_t *in;		/* pin: input */
    hal_bit_t *out;		/* pin: output */
    hal_float_t min;		/* parameter: low threashold */
    hal_float_t max;		/* parameter: high threashold */
} wcomp_t;

typedef struct {
    hal_float_t *in0;		/* pin: input0 (inverting) */
    hal_float_t *in1;		/* pin: input1 (non-inverting) */
    hal_bit_t *out;		/* pin: output */
    hal_float_t hyst;		/* parameter: hysteresis */
} comp_t;

typedef struct {
    hal_float_t *in0;		/* pin: input for X axis */
    hal_float_t *in1;		/* pin: input for Y axis */
    hal_float_t *in2;		/* pin: input for Z axis */
    hal_float_t *out;		/* pin: output */
} hypot_t;

typedef struct {
    hal_float_t *in0;		/* pin: input used when sel = 0 */
    hal_float_t *in1;		/* pin: input used when sel != 0 */
    hal_float_t *out;		/* pin: output */
    hal_bit_t *sel;		/* pin: select input */
} mux2_t;

typedef struct {
    hal_float_t *in0;		/* pin: input when sel1,0 = 0,0 */
    hal_float_t *in1;		/* pin: input when sel1,0 = 0,1 */
    hal_float_t *in2;		/* pin: input when sel1,0 = 1,0 */
    hal_float_t *in3;		/* pin: input when sel1,0 = 1,1 */
    hal_float_t *out;		/* pin: output */
    hal_bit_t *sel0;		/* pin: select input */
    hal_bit_t *sel1;		/* pin: select input */
} mux4_t;

typedef struct {
    hal_float_t *in0;		/* pin: input 0 */
    hal_float_t *in1;		/* pin: input 1 */
    hal_float_t *out;		/* pin: output */
    hal_float_t gain0;		/* param: gain for input 0 */
    hal_float_t gain1;		/* param: gain for input 1 */
} sum2_t;

typedef struct {
    hal_float_t *in0;		/* pin: input 0 */
    hal_float_t *in1;		/* pin: input 1 */
    hal_float_t *out;		/* pin: output */
} mult2_t;

typedef struct {
    hal_float_t *in;		/* pin: input */
    hal_float_t *out;		/* pin: output */
} integ_t;

typedef struct {
    hal_float_t *in;		/* pin: input */
    hal_float_t *out;		/* pin: output */
    float old;			/* internal state */
} ddt_t;

typedef struct {
    hal_float_t *in;		/* pin: input */
    hal_float_t *out;		/* pin: output */
    hal_float_t max;		/* param: maximum value */
    hal_float_t min;		/* param: minimum value */
} limit1_t;

typedef struct {
    hal_float_t *in;		/* pin: input */
    hal_float_t *out;		/* pin: output */
    hal_float_t max;		/* param: maximum value */
    hal_float_t min;		/* param: minimum value */
    hal_float_t maxv;		/* param: 1st derivative limit */
    double old_out;		/* previous output */
} limit2_t;

typedef struct {
    hal_float_t *in;		/* pin: input */
    hal_float_t *out;		/* pin: output */
    hal_float_t min;		/* param: minimum value */
    hal_float_t max;		/* param: maximum value */
    hal_float_t maxv;		/* param: 1st derivative limit */
    hal_float_t maxa;		/* param: 2nd derivative limit */
    double old_in;		/* previous input */
    double old_out;		/* previous output */
    double old_v;		/* previous 1st derivative */
} limit3_t;

typedef struct {
    hal_bit_t *ok_in;		/* pin: input, FALSE will trip */
    hal_bit_t *fault_in;	/* pin: input, TRUE will trip */
    hal_bit_t *ok_out;		/* pin: output, TRUE when OK */
    hal_bit_t *fault_out;	/* pin: output, TRUE when tripped */
    hal_bit_t *reset;		/* pin: latch reset (rising edge) */
    hal_bit_t *wd;		/* pin: watchdog/charge pump drive */
    hal_bit_t old_reset;	/* internal, for rising edge detect */
} estop_t;

typedef struct {
    hal_bit_t *in;		/* pin: input 0 */
    hal_bit_t *out;		/* pin: output  */
} not_t;

typedef struct {
    hal_bit_t *in0;		/* pin: input 0 */
    hal_bit_t *in1;		/* pin: input 1 */
    hal_bit_t *out;		/* pin: output  */
} and2_t;

typedef struct {
    hal_bit_t *in0;		/* pin: input 0 */
    hal_bit_t *in1;		/* pin: input 1 */
    hal_bit_t *out;		/* pin: output  */
} or2_t;

typedef struct {
    hal_float_t *in;		/* pin: input  */
    hal_float_t *out;		/* pin: output */
    hal_float_t gain;		/* param: gain */
    hal_float_t offset;		/* param: offset */
} scale_t;

typedef struct {
    hal_float_t *in;		/* pin: input  */
    hal_float_t *out;		/* pin: output */
    hal_float_t gain;		/* param: gain */
    double last_out;		/* prevoius output */
} lowpass_t;

typedef struct {
    hal_bit_t *a[8];		/* pins: input bits to compare */
    hal_bit_t *b[8];		/* pins: input bits to compare */
    hal_bit_t *in;		/* pin: cascade or enable input */
    hal_bit_t *out;		/* pin: output, TRUE if A matches B */
} match8_t;

typedef struct {
    hal_float_t *in;		/* pin: input */
    hal_float_t *min;		/* pin: minimum value */
    hal_float_t *max;		/* pin: maximum value */
    hal_bit_t *reset;		/* pin: reset input */
} minmax_t;


/* other globals */
static int comp_id;		/* component ID */

/***********************************************************************
*                  LOCAL FUNCTION DECLARATIONS                         *
************************************************************************/

static int export_constant(int num);
static int export_wcomp(int num);
static int export_comp(int num);
static int export_mux2(int num);
static int export_hypot(int num);
static int export_mux4(int num);
static int export_sum2(int num);
static int export_mult2(int num);
static int export_integ(int num);
static int export_ddt(int num);
static int export_limit1(int num);
static int export_limit2(int num);
static int export_limit3(int num);
static int export_estop(int num);
static int export_not(int num);
static int export_and2(int num);
static int export_or2(int num);
static int export_scale(int num);
static int export_lowpass(int num);
static int export_match8(int num);
static int export_minmax(int num);

static void constant_funct(void *arg, long period);
static void wcomp_funct(void *arg, long period);
static void comp_funct(void *arg, long period);
static void mux2_funct(void *arg, long period);
static void hypot_funct(void *arg, long period);
static void mux4_funct(void *arg, long period);
static void sum2_funct(void *arg, long period);
static void mult2_funct(void *arg, long period);
static void integ_funct(void *arg, long period);
static void ddt_funct(void *arg, long period);
static void limit1_funct(void *arg, long period);
static void limit2_funct(void *arg, long period);
static void limit3_funct(void *arg, long period);
static void estop_funct(void *arg, long period);
static void not_funct(void *arg, long period);
static void and2_funct(void *arg, long period);
static void or2_funct(void *arg, long period);
static void scale_funct(void *arg, long period);
static void lowpass_funct(void *arg, long period);
static void match8_funct(void *arg, long period);
static void minmax_funct(void *arg, long period);


/***********************************************************************
*                       INIT AND EXIT CODE                             *
************************************************************************/

int rtapi_app_main(void)
{
    int n;

    /* connect to the HAL */
    comp_id = hal_init("blocks");
    if (comp_id < 0) {
	rtapi_print_msg(RTAPI_MSG_ERR, "BLOCKS: ERROR: hal_init() failed\n");
	return -1;
    }
    /* allocate and export constants */
    if (constant > 0) {
	for (n = 0; n < constant; n++) {
	    if (export_constant(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_constant(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d constants\n", constant);
    }
    /* allocate and export window comparators */
    if (wcomp > 0) {
	for (n = 0; n < wcomp; n++) {
	    if (export_wcomp(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_wcomp(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d window comparators\n", wcomp);
    }
    /* allocate and export 2-input comparators */
    if (comp > 0) {
	for (n = 0; n < comp; n++) {
	    if (export_comp(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_comp(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 2-input comparators\n", comp);
    }
    /* allocate and export 2 input multiplexors */
    if (mux2 > 0) {
	for (n = 0; n < mux2; n++) {
	    if (export_mux2(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_mux2(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 2-input muxes\n", mux2);
    }
    /* allocate and export hypot() calculators */
    if (hypot > 0) {
	for (n = 0; n < hypot; n++) {
	    if (export_hypot(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_hypot(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d hypotenuse calculators\n", hypot);
    }
    /* allocate and export 4 input multiplexors */
    if (mux4 > 0) {
	for (n = 0; n < mux4; n++) {
	    if (export_mux4(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_mux4(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 4-input muxes\n", mux4);
    }
    /* allocate and export 2 input summers */
    if (sum2 > 0) {
	for (n = 0; n < sum2; n++) {
	    if (export_sum2(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_sum2(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 2-input summers\n", sum2);
    }
    /* allocate and export 2 input multipliers */
    if (mult2 > 0) {
	for (n = 0; n < mult2; n++) {
	    if (export_mult2(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_mult2(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 2-input multipliers\n", mult2);
    }
    /* allocate and export integrators */
    if (integ > 0) {
	for (n = 0; n < integ; n++) {
	    if (export_integ(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_integ(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 2 integrators\n", integ);
    }
    /* allocate and export differentiators */
    if (ddt > 0) {
	for (n = 0; n < ddt; n++) {
	    if (export_ddt(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_ddt(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d differentiators\n", ddt);
    }
    /* allocate and export first order limiters */
    if (limit1 > 0) {
	for (n = 0; n < limit1; n++) {
	    if (export_limit1(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_limit1(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d first order limiters\n", limit1);
    }
    /* allocate and export second order limiters */
    if (limit2 > 0) {
	for (n = 0; n < limit2; n++) {
	    if (export_limit2(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_limit2(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d second order limiters\n", limit2);
    }
    /* allocate and export third order limiters */
    if (limit3 > 0) {
	for (n = 0; n < limit3; n++) {
	    if (export_limit3(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_limit3(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d third order limiters\n", limit3);
    }
    /* allocate and export estop latch blocks */
    if (n_estop > 0) {
	for (n = 0; n < n_estop; n++) {
	    if (export_estop(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_estop(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d estop latch blocks\n", n_estop);
    }
    /* allocate and export 1 input logical nots */
    if (not > 0) {
	for (n = 0; n < not; n++) {
	    if (export_not(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_not(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d logical inverters\n", not);
    }
    /* allocate and export 2 input logical ands */
    if (and2 > 0) {
	for (n = 0; n < and2; n++) {
	    if (export_and2(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_and2(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 2-input logical ands\n", and2);
    }
    /* allocate and export 2 input logical ors */
    if (or2 > 0) {
	for (n = 0; n < or2; n++) {
	    if (export_or2(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_or2(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d 2-input logical ors\n", or2);
    }
    /* allocate and export scalers */
    if (scale > 0) {
	for (n = 0; n < scale; n++) {
	    if (export_scale(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_scale(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d scalers\n", scale);
    }
    /* allocate and export lowpass filters */
    if (lowpass > 0) {
	for (n = 0; n < lowpass; n++) {
	    if (export_lowpass(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_lowpass(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d lowpass filters\n", lowpass);
    }
    /* allocate and export binary match detectors */
    if (match8 > 0) {
	for (n = 0; n < match8; n++) {
	    if (export_match8(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_match8(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d match detectors\n", match8);
    }
    /* allocate and export min/max blocks */
    if (minmax > 0) {
	for (n = 0; n < minmax; n++) {
	    if (export_minmax(n) != 0) {
		rtapi_print_msg(RTAPI_MSG_ERR,
		    "BLOCKS: ERROR: export_minmax(%d) failed\n", n);
		hal_exit(comp_id);
		return -1;
	    }
	}
	rtapi_print_msg(RTAPI_MSG_INFO,
	    "BLOCKS: installed %d min/max blocks\n", minmax);
    }
    hal_ready(comp_id);
    return 0;
}

void rtapi_app_exit(void)
{
    hal_exit(comp_id);
}

/***********************************************************************
*                     REALTIME BLOCK FUNCTIONS                         *
************************************************************************/

static void constant_funct(void *arg, long period)
{
    constant_t *constant;

    /* point to block data */
    constant = (constant_t *) arg;
    /* calculate output */
    *(constant->out) = constant->value;
}

static void wcomp_funct(void *arg, long period)
{
    wcomp_t *wcomp;
    float tmp;

    /* point to block data */
    wcomp = (wcomp_t *) arg;
    /* calculate output */
    tmp = *(wcomp->in);
    if ((wcomp->min < tmp) && (tmp < wcomp->max)) {
	*(wcomp->out) = 1;
    } else {
	*(wcomp->out) = 0;
    }
}

static void comp_funct(void *arg, long period)
{
    comp_t *comp;
    float tmp;

    /* point to block data */
    comp = (comp_t *) arg;
    /* calculate output */
    tmp = *(comp->in1) - *(comp->in0);
    if (*(comp->out)) {
	if (tmp < -comp->hyst/2.) {
	    *(comp->out) = 0;
	}
    } else {
	if (tmp > comp->hyst/2.) {
	    *(comp->out) = 1;
	}
    }
}

static void hypot_funct(void *arg, long period)
{
    double a, b, c;
    hypot_t *hypot;

    /* point to block data */
    hypot = (hypot_t *) arg;

    /* grab inputs to locals */
    a = *(hypot->in0);
    b = *(hypot->in1);
    c = *(hypot->in2);

    /* calculate output */
    *(hypot->out) = sqrt(a*a + b*b + c*c);
}

static void mux2_funct(void *arg, long period)
{
    mux2_t *mux2;

    /* point to block data */
    mux2 = (mux2_t *) arg;
    /* calculate output */
    if (*(mux2->sel)) {
	*(mux2->out) = *(mux2->in1);
    } else {
	*(mux2->out) = *(mux2->in0);
    }
}

static void mux4_funct(void *arg, long period)
{
    mux4_t *mux4;

    /* point to block data */
    mux4 = (mux4_t *) arg;
    /* calculate output */
    if (*(mux4->sel1)) {
	if (*(mux4->sel0)) {
	    *(mux4->out) = *(mux4->in3);
	} else {
	    *(mux4->out) = *(mux4->in2);
	}
    } else {
	if (*(mux4->sel0)) {
	    *(mux4->out) = *(mux4->in1);
	} else {
	    *(mux4->out) = *(mux4->in0);
	}
    }
}

static void sum2_funct(void *arg, long period)
{
    sum2_t *sum2;

    /* point to block data */
    sum2 = (sum2_t *) arg;
    /* calculate output */
    *(sum2->out) = *(sum2->in0) * sum2->gain0 + *(sum2->in1) * sum2->gain1;
}

static void mult2_funct(void *arg, long period)
{
    mult2_t *mult2;

    /* point to block data */
    mult2 = (mult2_t *) arg;
    /* calculate output */
    *(mult2->out) = *(mult2->in0) * *(mult2->in1);
}

static void integ_funct(void *arg, long period)
{
    integ_t *integ;

    /* point to block data */
    integ = (integ_t *) arg;
    /* calculate output */
    *(integ->out) += *(integ->in) * (period * 0.000000001);
}

static void ddt_funct(void *arg, long period)
{
    ddt_t *ddt;
    float tmp;

    /* point to block data */
    ddt = (ddt_t *) arg;
    /* calculate output */
    tmp = *(ddt->in);
    *(ddt->out) = (tmp - ddt->old) / (period * 0.000000001);
    ddt->old = tmp;
}

static void limit1_funct(void *arg, long period)
{
    limit1_t *limit1;
    double out;

    /* point to block data */
    limit1 = (limit1_t *) arg;
    /* apply first order limit */
    out = *(limit1->in);
    if ( out < limit1->min ) {
	out = limit1->min;
    }
    if ( out > limit1->max ) {
	out = limit1->max;
    }
    *(limit1->out) = out;
}

static void limit2_funct(void *arg, long period)
{
    limit2_t *limit2;
    double out, delta, tmp;

    /* point to block data */
    limit2 = (limit2_t *) arg;
    /* apply first order limit */
    out = *(limit2->in);
    if ( out < limit2->min ) {
	out = limit2->min;
    }
    if ( out > limit2->max ) {
	out = limit2->max;
    }
    /* apply second order limit */
    delta = limit2->maxv * (period * 0.000000001);
    tmp = limit2->old_out - delta;
    if ( out < tmp ) {
	out = tmp;
    }
    tmp = limit2->old_out + delta;
    if ( out > tmp ) {
	out = tmp;
    }
    limit2->old_out = out;
    *(limit2->out) = out;
}

static void limit3_funct(void *arg, long period)
{
    limit3_t *limit3;
    double in, out, dt, in_v, min_v, max_v, ramp_a, avg_v, err, dv, dp;
    double min_out, max_out, match_time, est_in, est_out;

    est_in = est_out = match_time = 0;
    
    /* point to block data */
    limit3 = (limit3_t *) arg;
    /* apply first order limit */
    in = *(limit3->in);
    if ( in < limit3->min ) {
	in = limit3->min;
    }
    if ( in > limit3->max ) {
	in = limit3->max;
    }
    /* calculate input derivative */
    dt = period * 0.000000001;
    in_v = (in - limit3->old_in) / dt;
    /* determine v and out that can be reached in one period */
    min_v = limit3->old_v - limit3->maxa * dt;
    if ( min_v < -limit3->maxv ) {
	min_v = -limit3->maxv;
    }
    max_v = limit3->old_v + limit3->maxa * dt;
    if ( max_v > limit3->maxv ) {
	max_v = limit3->maxv;
    }
    min_out = limit3->old_out + min_v * dt;
    max_out = limit3->old_out + max_v * dt;
    if ( ( in >= min_out ) && ( in <= max_out ) && ( in_v >= min_v ) && ( in_v <= max_v ) ) {
	/* we can follow the command without hitting a limit */
	out = in;
	limit3->old_v = ( out - limit3->old_out ) / dt;
    } else {
	/* can't follow commanded path while obeying limits */ 
	/* determine which way we need to ramp to match v */
	if ( in_v > limit3->old_v ) {
	    ramp_a = limit3->maxa;
	} else {
	    ramp_a = -limit3->maxa;
	}
	/* determine how long the match would take */
	match_time = ( in_v - limit3->old_v ) / ramp_a;
	/* where we will be at the end of the match */
	avg_v = ( in_v + limit3->old_v + ramp_a * dt ) * 0.5;
	est_out = limit3->old_out + avg_v * match_time;
	/* calculate the expected command position at that time */
	est_in = limit3->old_in + in_v * match_time;
	/* calculate position error at that time */
	err = est_out - est_in;
	/* calculate change in final position if we ramp in the
	   opposite direction for one period */
	dv = -2.0 * ramp_a * dt;
	dp = dv * match_time;
	
	/* decide what to do */
	if ( fabs(err+dp*2.0) < fabs(err) ) {
	    ramp_a = -ramp_a;
	}
		
		
	if ( ramp_a < 0.0 ) {
	    out = min_out;
	    limit3->old_v = min_v;
	} else {
	    out = max_out;
	    limit3->old_v = max_v;
	}
    }    
    limit3->old_out = out;
    limit3->old_in = in;
    *(limit3->out) = out;
}

/* this module sets 'out' true when 'in' is true _and_ it sees
   a rising edge on 'reset'.  If 'in' goes false, it sets 'out'
   false.  It also toggles 'watchdog' constantly, which should
   be used for a charge pump or other such circuit.  Note that
   this block should _not_ be relied on to turn off 'out', 
   instead, 'in' should be daisy-chained through 'out' in
   hardware, and this module is only used to prevent a restart
   if/when 'in' comes back on.
*/

static void estop_funct(void *arg, long period)
{
    estop_t *estop;

    /* point to block data */
    estop = (estop_t *) arg;

    /* check inputs */
    if (( *(estop->ok_in) != 0 ) && ( *(estop->fault_in) == 0 )) {
	/* no fault conditions, check for reset edge */
	if (( *(estop->reset) != 0 ) && ( estop->old_reset == 0 )) {
	    /* got a rising edge, indicate "OK" on outputs */
	    *(estop->ok_out) = 1;
	    *(estop->fault_out) = 0;
	}
	/* toggle watchdog */
	if ( *(estop->wd) == 0 ) {
	    *(estop->wd) = 1;
	} else {
	    *(estop->wd) = 0;
	}
    } else {
	/* fault condition exists, trip */
	*(estop->ok_out) = 0;
	*(estop->fault_out) = 1;
    }
    /* store state of reset input for next pass (for edge detect) */
    estop->old_reset = *(estop->reset);
}


static void not_funct(void *arg, long period)
{
    not_t *not;

    /* point to block data */
    not = (not_t *) arg;
    /* calculate output */
    *(not->out) = ! *(not->in);
}

static void and2_funct(void *arg, long period)
{
    and2_t *and2;

    /* point to block data */
    and2 = (and2_t *) arg;
    /* calculate output */
    *(and2->out) = *(and2->in0) && *(and2->in1);
}

static void or2_funct(void *arg, long period)
{
    or2_t *or2;

    /* point to block data */
    or2 = (or2_t *) arg;
    /* calculate output */
    *(or2->out) = *(or2->in0) || *(or2->in1);
}

static void scale_funct(void *arg, long period)
{
    scale_t *scale;

    /* point to block data */
    scale = (scale_t *) arg;
    /* calculate output */
    *(scale->out) = *(scale->in) * scale->gain + scale->offset;
}

static void lowpass_funct(void *arg, long period)
{
    lowpass_t *lowpass;

    /* point to block data */
    lowpass = (lowpass_t *) arg;
    /* calculate output */
    *(lowpass->out) += (*(lowpass->in) - *(lowpass->out)) * lowpass->gain;
}

static void match8_funct(void *arg, long period)
{
    match8_t *match8;
    int n;
    hal_bit_t a, b, tmp;

    /* point to block data */
    match8 = (match8_t *) arg;
    /* calculate output, starting with cascade/enable input */
    if ( *(match8->in) != 0 ) {
	/* input true, process a and b */
	tmp = 1;
	n = 0;
    } else {
	/* input false, ignore a and b */
	tmp = 0;
	n = 8;
    }
    /* test all bits, exit loop as soon as mismatch found */
    while ( n < 8 ) {
	a = *(match8->a[n]);
	b = *(match8->b[n]);
	if ((( a == 0 ) && ( b != 0 )) || (( a != 0 ) && ( b == 0 ))) {
	    tmp = 0;
	    n = 8;
	}
	n++;
    }
    /* write output */
    *(match8->out) = tmp;
}

static void minmax_funct(void *arg, long period)
{
    minmax_t *minmax;
    float in;

    /* point to block data */
    minmax = (minmax_t *) arg;
    /* get input to local */
    in = *(minmax->in);
    if ( *(minmax->reset) ) {
	/* when reset, min and max both track input */
	*(minmax->min) = in;
	*(minmax->max) = in;
    } else {
	if ( in < *(minmax->min) ) {
	    *(minmax->min) = in;
	}
	if ( in > *(minmax->max) ) {
	    *(minmax->max) = in;
	}
    }
}

/***********************************************************************
*                   LOCAL FUNCTION DEFINITIONS                         *
************************************************************************/

static int export_constant(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    constant_t *constant;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for constant */
    constant = hal_malloc(sizeof(constant_t));
    if (constant == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "constant.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(constant->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export param for value */
    rtapi_snprintf(buf, HAL_NAME_LEN, "constant.%d.value", num);
    retval = hal_param_float_new(buf, HAL_RW, &(constant->value), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "constant.%d", num);
    retval = hal_export_funct(buf, constant_funct, constant, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    constant->value = 1.0;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_wcomp(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    wcomp_t *wcomp;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for window comparator */
    wcomp = hal_malloc(sizeof(wcomp_t));
    if (wcomp == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "wcomp.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(wcomp->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "wcomp.%d.out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(wcomp->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params for min and max */
    rtapi_snprintf(buf, HAL_NAME_LEN, "wcomp.%d.min", num);
    retval = hal_param_float_new(buf, HAL_RW, &(wcomp->min), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "wcomp.%d.max", num);
    retval = hal_param_float_new(buf, HAL_RW, &(wcomp->max), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "wcomp.%d", num);
    retval = hal_export_funct(buf, wcomp_funct, wcomp, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    wcomp->min = -1.0;
    wcomp->max = 1.0;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_comp(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    comp_t *comp;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 2-input comparator */
    comp = hal_malloc(sizeof(comp_t));
    if (comp == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "comp.%d.in0", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(comp->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "comp.%d.in1", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(comp->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "comp.%d.out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(comp->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params for hystersis */
    rtapi_snprintf(buf, HAL_NAME_LEN, "comp.%d.hyst", num);
    retval = hal_param_float_new(buf, HAL_RW, &(comp->hyst), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "comp.%d", num);
    retval = hal_export_funct(buf, comp_funct, comp, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    comp->hyst = 0.0;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_hypot(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    hypot_t *h;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for hypotenuse calculator */
    h = hal_malloc(sizeof(hypot_t));
    if (h == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "hypot.%d.in0", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(h->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "hypot.%d.in1", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(h->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "hypot.%d.in2", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(h->in2), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "hypot.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(h->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "hypot.%d", num);
    retval = hal_export_funct(buf, hypot_funct, h, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_mux2(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    mux2_t *mux2;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 2 input multiplexor */
    mux2 = hal_malloc(sizeof(mux2_t));
    if (mux2 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux2.%d.in0", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mux2->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux2.%d.in1", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mux2->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux2.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(mux2->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for select input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux2.%d.sel", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(mux2->sel), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux2.%d", num);
    retval = hal_export_funct(buf, mux2_funct, mux2, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_mux4(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    mux4_t *mux4;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 4 input multiplexor */
    mux4 = hal_malloc(sizeof(mux4_t));
    if (mux4 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d.in0", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mux4->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d.in1", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mux4->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d.in2", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mux4->in2), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d.in3", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mux4->in3), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(mux4->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pins for select input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d.sel0", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(mux4->sel0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d.sel1", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(mux4->sel1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mux4.%d", num);
    retval = hal_export_funct(buf, mux4_funct, mux4, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_sum2(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    sum2_t *sum2;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 2-input summer */
    sum2 = hal_malloc(sizeof(sum2_t));
    if (sum2 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "sum2.%d.in0", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(sum2->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "sum2.%d.in1", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(sum2->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "sum2.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(sum2->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params for gains */
    rtapi_snprintf(buf, HAL_NAME_LEN, "sum2.%d.gain0", num);
    retval = hal_param_float_new(buf, HAL_RW, &(sum2->gain0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "sum2.%d.gain1", num);
    retval = hal_param_float_new(buf, HAL_RW, &(sum2->gain1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "sum2.%d", num);
    retval = hal_export_funct(buf, sum2_funct, sum2, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    sum2->gain0 = 1.0;
    sum2->gain1 = 1.0;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_mult2(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    mult2_t *mult2;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 2-input multiplier */
    mult2 = hal_malloc(sizeof(mult2_t));
    if (mult2 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mult2.%d.in0", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mult2->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "mult2.%d.in1", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(mult2->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mult2.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(mult2->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "mult2.%d", num);
    retval = hal_export_funct(buf, mult2_funct, mult2, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_integ(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    integ_t *integ;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for integrator */
    integ = hal_malloc(sizeof(integ_t));
    if (integ == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "integ.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(integ->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "integ.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(integ->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "integ.%d", num);
    retval = hal_export_funct(buf, integ_funct, integ, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_ddt(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    ddt_t *ddt;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for differentiator */
    ddt = hal_malloc(sizeof(ddt_t));
    if (ddt == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "ddt.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(ddt->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "ddt.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(ddt->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "ddt.%d", num);
    retval = hal_export_funct(buf, ddt_funct, ddt, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_limit1(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    limit1_t *limit1;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for first order limiter */
    limit1 = hal_malloc(sizeof(limit1_t));
    if (limit1 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit1.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(limit1->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit1.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(limit1->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit1.%d.min", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit1->min), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit1.%d.max", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit1->max), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit1.%d", num);
    retval = hal_export_funct(buf, limit1_funct, limit1, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    limit1->min = -1e20;
    limit1->max =  1e20;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_limit2(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    limit2_t *limit2;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for second order limiter */
    limit2 = hal_malloc(sizeof(limit2_t));
    if (limit2 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit2.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(limit2->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit2.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(limit2->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit2.%d.min", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit2->min), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit2.%d.max", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit2->max), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit2.%d.maxv", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit2->maxv), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit2.%d", num);
    retval = hal_export_funct(buf, limit2_funct, limit2, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    limit2->min = -1e20;
    limit2->max =  1e20;
    limit2->maxv = 1e20;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_limit3(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    limit3_t *limit3;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for third order limiter */
    limit3 = hal_malloc(sizeof(limit3_t));
    if (limit3 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit3.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(limit3->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit3.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(limit3->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit3.%d.min", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit3->min), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit3.%d.max", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit3->max), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit3.%d.maxv", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit3->maxv), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit3.%d.maxa", num);
    retval = hal_param_float_new(buf, HAL_RW, &(limit3->maxa), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "limit3.%d", num);
    retval = hal_export_funct(buf, limit3_funct, limit3, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    limit3->min = -1e20;
    limit3->max =  1e20;
    limit3->maxv = 1e20;
    limit3->maxa = 1e20;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_estop(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    estop_t *estop;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for an estop latch */
    estop = hal_malloc(sizeof(estop_t));
    if (estop == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "estop.%d.ok-in", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(estop->ok_in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "estop.%d.fault-in", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(estop->fault_in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "estop.%d.reset", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(estop->reset), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pins for outputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "estop.%d.ok-out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(estop->ok_out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "estop.%d.fault-out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(estop->fault_out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "estop.%d.watchdog", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(estop->wd), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "estop.%d", num);
    retval = hal_export_funct(buf, estop_funct, estop, 0, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* initialize internal vars */
    estop->old_reset = 1;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_not(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    not_t *not;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 1-input logical not */
    not = hal_malloc(sizeof(not_t));
    if (not == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "not.%d.in", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(not->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "not.%d.out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(not->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "not.%d", num);
    retval = hal_export_funct(buf, not_funct, not, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_and2(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    and2_t *and2;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 2-input logical and */
    and2 = hal_malloc(sizeof(and2_t));
    if (and2 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "and2.%d.in0", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(and2->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "and2.%d.in1", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(and2->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "and2.%d.out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(and2->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "and2.%d", num);
    retval = hal_export_funct(buf, and2_funct, and2, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_or2(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    or2_t *or2;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for 2-input logical or */
    or2 = hal_malloc(sizeof(or2_t));
    if (or2 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "or2.%d.in0", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(or2->in0), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "or2.%d.in1", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(or2->in1), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "or2.%d.out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(or2->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "or2.%d", num);
    retval = hal_export_funct(buf, or2_funct, or2, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_scale(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    scale_t *scale;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for scaler */
    scale = hal_malloc(sizeof(scale_t));
    if (scale == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "scale.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(scale->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "scale.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(scale->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params for gains */
    rtapi_snprintf(buf, HAL_NAME_LEN, "scale.%d.gain", num);
    retval = hal_param_float_new(buf, HAL_RW, &(scale->gain), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "scale.%d.offset", num);
    retval = hal_param_float_new(buf, HAL_RW, &(scale->offset), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "scale.%d", num);
    retval = hal_export_funct(buf, scale_funct, scale, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    scale->gain   = 1.0;
    scale->offset = 0.0;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}


static int export_lowpass(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    lowpass_t *lowpass;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for lowpass filter */
    lowpass = hal_malloc(sizeof(lowpass_t));
    if (lowpass == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pins for inputs */
    rtapi_snprintf(buf, HAL_NAME_LEN, "lowpass.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(lowpass->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "lowpass.%d.out", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(lowpass->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export params for gains */
    rtapi_snprintf(buf, HAL_NAME_LEN, "lowpass.%d.gain", num);
    retval = hal_param_float_new(buf, HAL_RW, &(lowpass->gain), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' param export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "lowpass.%d", num);
    retval = hal_export_funct(buf, lowpass_funct, lowpass, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* set default parameter values */
    lowpass->gain = 1.0;
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_match8(int num)
{
    int retval, msg, n;
    char buf[HAL_NAME_LEN + 2];
    match8_t *match8;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for match detector filter */
    match8 = hal_malloc(sizeof(match8_t));
    if (match8 == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for cascade/enable input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "match8.%d.in", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(match8->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    *match8->in = 1;
    /* export pins for A and B inputs */
    for ( n = 0 ; n < 8 ; n++ ) {
	rtapi_snprintf(buf, HAL_NAME_LEN, "match8.%d.a%1d", num, n);
	retval = hal_pin_bit_new(buf, HAL_IN, &(match8->a[n]), comp_id);
	if (retval != 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR,
		"BLOCKS: ERROR: '%s' pin export failed\n", buf);
	    return retval;
	}
	rtapi_snprintf(buf, HAL_NAME_LEN, "match8.%d.b%1d", num, n);
	retval = hal_pin_bit_new(buf, HAL_IN, &(match8->b[n]), comp_id);
	if (retval != 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR,
		"BLOCKS: ERROR: '%s' pin export failed\n", buf);
	    return retval;
	}
    }
    /* export pin for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "match8.%d.out", num);
    retval = hal_pin_bit_new(buf, HAL_OUT, &(match8->out), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "match8.%d", num);
    retval = hal_export_funct(buf, match8_funct, match8, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}

static int export_minmax(int num)
{
    int retval, msg;
    char buf[HAL_NAME_LEN + 2];
    minmax_t *minmax;

    /* This function exports a lot of stuff, which results in a lot of
       logging if msg_level is at INFO or ALL. So we save the current value
       of msg_level and restore it later.  If you actually need to log this
       function's actions, change the second line below */
    msg = rtapi_get_msg_level();
    rtapi_set_msg_level(RTAPI_MSG_WARN);

    /* allocate shared memory for min/max block */
    minmax = hal_malloc(sizeof(minmax_t));
    if (minmax == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: hal_malloc() failed\n");
	return -1;
    }
    /* export pin for input */
    rtapi_snprintf(buf, HAL_NAME_LEN, "minmax.%d.in", num);
    retval = hal_pin_float_new(buf, HAL_IN, &(minmax->in), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pins for output */
    rtapi_snprintf(buf, HAL_NAME_LEN, "minmax.%d.min", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(minmax->min), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    rtapi_snprintf(buf, HAL_NAME_LEN, "minmax.%d.max", num);
    retval = hal_pin_float_new(buf, HAL_OUT, &(minmax->max), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export pin for reset */
    rtapi_snprintf(buf, HAL_NAME_LEN, "minmax.%d.reset", num);
    retval = hal_pin_bit_new(buf, HAL_IN, &(minmax->reset), comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' pin export failed\n", buf);
	return retval;
    }
    /* export function */
    rtapi_snprintf(buf, HAL_NAME_LEN, "minmax.%d", num);
    retval = hal_export_funct(buf, minmax_funct, minmax, 1, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "BLOCKS: ERROR: '%s' funct export failed\n", buf);
	return -1;
    }
    /* restore saved message level */
    rtapi_set_msg_level(msg);
    return 0;
}