#include <stdlib.h>
#include <stdio.h>
#include <assert.h>


static int dm2,dm1,dm0,dr;
static int new_dm2,new_dm1,new_dm0,new_dr;
static int dir; /* -1: towards 0, 1: towards 1, 0: hasn't changed yet */

static int a,b;


static struct history {
    int value;
    const char *reg_name;
} history[100];

static int entries;


enum value {
    VALUE_0,
    VALUE_0R,
    VALUE_Z,
    VALUE_1R,
    VALUE_1,
};


static const char *value_name[] = {
    "0", "0R", "Z", "1R", "1"
};


static const enum value value[] = {
    VALUE_0R, VALUE_1,
    VALUE_0,  VALUE_1,
    VALUE_Z,  VALUE_Z,
    VALUE_0,  VALUE_1R,
    VALUE_Z,  VALUE_1,
    VALUE_0,  VALUE_1,
    VALUE_Z,  VALUE_Z,
    VALUE_0,  VALUE_Z,
};


static int pack_current(void)
{
    return (dm2 << 3) | (dm1 << 2) | (dm0 << 1) | dr;
}


static enum value decode_current(void)
{
    return value[pack_current()];
}


static void print_value(FILE *file,int v)
{
    fprintf(stderr,"DM210 %d%d%d DR %d (%s)",
      (v >> 3) & 1,(v >> 2) & 1,(v >> 1) & 1,v & 1,
      value_name[value[v]]);
}


static void complain(int *reg,const char *reg_name)
{
    int i;

    print_value(stderr,a);
    fprintf(stderr," -> ");
    print_value(stderr,b);
    fprintf(stderr,":\n");
    for (i = 0; i != entries; i++) {
	print_value(stderr,history[i].value);
	fprintf(stderr," (%s)\n",history[i].reg_name);
    }
    fprintf(stderr,"BOOM!\n");
    exit(1);
}


static void set(int *reg,int value,const char *reg_name)
{
    enum value old,new;

    value = value & 1; /* allow use of bit ops */
    old = decode_current();
    *reg = value;
    new = decode_current();
    history[entries].value = pack_current();
    history[entries].reg_name = reg_name;
    entries++;
    if (old == new)
	return;
    if (new > old) {
	if (dir < 0)
	    complain(reg,reg_name);
	dir = 1;
    }
    else {
	if (dir > 0)
	    complain(reg,reg_name);
	dir = -1;
    }
}


#define SET(reg,value) \
    set(&reg,(value),#reg)


static void doit(void)
{
    SET(dm0,dm0 & ~new_dm0);
    SET(dm1,dm1 | new_dm1);
    SET(dm2,dm2 | new_dm2);
    SET(dr,new_dr);
    SET(dm0,new_dm0);
    SET(dm1,new_dm1);
    SET(dm2,new_dm2);
}


static void transition(int from,int to)
{
    dm2 = (from >> 3) & 1;
    dm1 = (from >> 2) & 1;
    dm0 = (from >> 1) & 1;
    dr = from & 1;
    new_dm2 = (to >> 3) & 1;
    new_dm1 = (to >> 2) & 1;
    new_dm0 = (to >> 1) & 1;
    new_dr = to & 1;
    dir = 0;
    entries = 0;
    doit();
    assert(dm2 == new_dm2);
    assert(dm1 == new_dm1);
    assert(dm0 == new_dm0);
    assert(dr == new_dr);
}


int main(void)
{
    /* not != 16, because we may use increments > 1 */
    for (a = 0; a < 16; a += 2)
	for (b = 0; b < 16; b += 2)
	    transition(a,b);
    return 0;
}
