Hogwarts Wand Docs: ../server/fmtb_parse.c

Wand Sourcecode: ../server/fmtb_parse.c

//
// fmtb_parse.c - read a wpic*.txt file (stdin) and parse into motion codes (stdout)
//
// Copyright (C) 2006  Nathan (Acorn) Pooley
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License (gpl.txt) for more details. 
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// You can contact the author, Nathan (Acorn) Pooley, by writing
// to Nathan (Acorn) Pooley, 949 Buckeye Drive, Sunnyvale, CA  94086, USA
// or through the form at http://www.rawbw.com/~acorn/wand/feedback.html
//

//#@DOC@ read a wpic*.txt spell file (stdin) and parse into motion tokens

//###########################################################################
//############################### INCLUDES ##################################
//###########################################################################

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#include "picsim.h"
#include "ac_assert.h"

#include "spells.h"

Spell spells[] = {
#include "spells.c"
    {0},
};

//###########################################################################
//############################### DEFINES ###################################
//###########################################################################



#define USE_PIC_PARSE1      1
#define USE_PIC_FILLGAP     1
#define USE_PIC_FINDWEAK    1
#define USE_PIC_FINDPULSE   1
#define USE_PIC_MARKLAST    1
#define USE_PIC_TOKENIZE    1


#define DBG_PARSE1_MINIMAL  0
#define DBG_FILLGAP_MINIMAL 0
#define DBG_FINDWEAK        0
#define DBG_FINDPULSE       0
#define DBG_MARKLAST        0
#define DBG_TOKENIZE        0
#define DBG_LOADSPELL       0
#define DBG_SCORETOK        0
#define DBG_SCORESPELL      0
#define DBG_CHECKSPELLS     0






#define MAX_TOKENS  1000

#define MINIMAL     1

#define ASSERT(c)   AC_ASSERT(c)

#define DEBUG   0

#define CNT_MAX 1024


#define ABS(a)   ((a)<0?-(a):(a))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))


#define TH_T_STILL      0.25
#define TH_A_JUMP       5
#define TH_A_NEWQUAD    7
#define TH_A_SNAPQUAD   6
#define TH_A_CONNECT    2



//###########################################################################
//############################### TYPEDEFS ##################################
//###########################################################################

typedef struct PSpellRec {
    int flags;
    int maxscore;
    int holdoff;
    const char *name;
    char *baseName;
    const char *mwanemes;
    unsigned char tokens[MAX_TOKENS];
    int len;
    int score;
    int spellinfo;
    int id;
    int sortId;
    unsigned char pic_mwanemes[MAX_TOKENS];
    unsigned char pic_tokens[MAX_TOKENS];
} PSpell;


typedef struct Motion2Rec {
    
    int shape;
    int imp;
    int maxmag;
    int angle;  // 0xc0=end 0xa0=start
    

    //
    // strictly for debug
    //
    int dbg_atime;
} Motion2;

//NOTE: dir & MONO are required in PULSE shapes too


typedef enum MotionFlagsEnum {
    MFLG_ZERO           =   0x01,   // only used in parse
    MFLG_MONO           =   0x02,   // NEEDED
    MFLG_CIRCLE         =   0x04,   // NEEDED
    MFLG_BEST_MAG       =   0x08,   // not really used
    MFLG_BEST_IMP       =   0x10,   // not really used
    MFLG_MISSING_CIRCLE =   0x20,   // unused
    MFLG_PREV_PAUSE     =   0x40,   // ?
    MFLG_NEXT_PAUSE     =   0x80,   // ?

    MFLG_END            =   0xff
} MotionFlags;

typedef struct MotionRec {
    int     b_atime;    // not needed
    int     b_dtime;    // not needed
    int     b_imp;
    int     b_minmag;   // only used for deltamag
    int     b_maxmag;
    int     b_deltamag; // only used in parse1
    int     b_angle;
    int     b_flags;

    struct MotionRec *c_next;
    struct MotionRec *c_prev;
    int c_shape;
    int c_dir;
    int c_imp;

#if !MINIMAL
    int c_time;
    int c_oct;
    int c_best_angle;
    int c_best_mag;
    int c_cnt;
    int c_isum;
    int c_maxstep;
    int c_maxdmag;
    int c_asum;
    int c_flags;
#endif

    int token;
} Motion;

typedef struct SpellCreateInfoRec {
    int     lastAngle;
    int     prevCircleCnt;
} SpellCreateInfo;

//###########################################################################
//############################### PROTOTYPES ################################
//###########################################################################


static int setSilent(int val);
static void printMotion(char *str, Motion *mlist, Motion *m, int color);
static void printMotion2(char *str, Motion2 *mlist, Motion2 *m, int color);
static void breakpt(void);
static char *colorStr(int bg);
static char *spellTokenStr(int tok);
static char *motionTokenStr(unsigned int tok);

//###########################################################################
//############################### GLOBALS ###################################
//###########################################################################

char *OPT_input_filename=0;
int OPT_raw = 0;
int OPT_result_motion_only = 0;
int OPT_result_spell_only = 0;
int OPT_export = 0;
int OPT_showspell = 0;
int OPT_filelist = 0;
int OPT_vital = 0;
int OPT_allspells = 0;
int OPT_describe = 0;
int OPT_quick = 0;
int OPT_showtime = 0;
int OPT_silent = 0;
int OPT_silent2 = 0;
int OPT_use_original = 0;
int OPT_use_minimal = 0;
int OPT_use_picsim = 0;
int OPT_use_classic = 0;
int OPT_use_experimental = 0;
int OPT_use_all = 0;
int OPT_show_tokens = 0;
int OPT_nocolor = 0;
int OPT_noaddr = 0;
int OPT_nocrash = 0;
int OPT_onespell = 0;
int OPT_no_orig = 1;
int OPT_crosscheck = 0;
int OPT_check_comparetok = 1;
int OPT_do_picsim = 1;
char *OPT_result_filename = 0;
char *OPT_mwaneme_filename = 0;

char gCmdline[1000] = "";
PSpell *gCurrentSpell = 0;
int     gCurrentMwanemeIndex = 0;
int     gCompareCnt = 0;
picChip *chip = 0;

static PSpell *gBestSpell1_m = 0;
static PSpell *gBestSpell2_m = 0;
static PSpell *gChooseSpell_m = 0;
static PSpell *gBestSpell1 = 0;
static PSpell *gBestSpell2 = 0;
static PSpell *gChooseSpell = 0;

static int gMaxMaxscore = 0;
static int gMaxHoldoff = 0;


static char *quadStr[] = {
    "Down ",
    "Right",
    "Up   ",
    "Left ",
};

static char *octStr[] = {
    "d ",
    "dr",
    " r",
    "ur",
    "u ",
    "ul",
    " l",
    "dl",
};

static char *octStr2[] = {
    "v",
    ".",
    ">",
    "'",
    "^",
    "`",
    "<",
    ",",
};
static char *motionShapeStr[] = {
    MOTION_SHAPE_STRING()
};
#if 0
static char *spellShapeStr[] = {
    MOTION_SHAPE_STRING()
};
#endif


//
// score for a spell-shape that is skipped (not matched by any motion-shape)
//
static int ignoreSpells[8] = {
        100,    // SSHP_PAUSE
        15,     // SSHP_CIRCLE
        0,      // SSHP_OCIRCLE
        1,      // SSHP_OPULSE
        100,    // SSHP_PULSE
        0,      // SSHP_WILD
        99999,
        99999,
};

//
// score for a motion-shape that is skipped (not matched by any spell-shape)
//
static int ignoreMotions[8] = {
        1,      // MSHP_DIRTY_CIRCLE,
        20,     // MSHP_DIRTY_CIRCLE_PLUS,
        1,      // MSHP_CLEAN_CIRCLE,
        100,    // MSHP_CLEAN_CIRCLE_PLUS,
        1,      // MSHP_PULSE,
        100,    // MSHP_PULSE_PLUS,
        0,      // MSHP_CIRCLE_GAP,
        0,      // MSHP_PAUSE,
};

//
// NO LONGER USED - no spell pauses
//
static int scorePause[8] = {
        0,      // MSHP_PAUSE,
        30,     // MSHP_CLEAN_CIRCLE,
        1000,   // MSHP_CLEAN_CIRCLE_PLUS,
        30,     // MSHP_DIRTY_CIRCLE,
        1000,   // MSHP_DIRTY_CIRCLE_PLUS,
        30,     // MSHP_PULSE,
        1000,   // MSHP_PULSE_PLUS,
        0,      // MSHP_CIRCLE_GAP
};

static int scorePulse[5] = {
        0,      // same dir             *
        0,      // off by 2             *
        0,      // off by 4 (45 deg)    *
        6,      // off by 6             *
        1000,   // off by 8 (90 deg)    *
};
static int scoreCircleMatch = 0;
static int scoreCircleMiss  = 1000;
static int scorePulseMiss   = 1000;

//###########################################################################
//############################### PIC DEFS ##################################
//###########################################################################

#define PIC_CSIM    1

int v_mo_oct;
int v_mo_angle;
int v_mo_impl;
int v_mo_imph;
int v_mo_maxmag;
int v_mo_shape;
int v_mo_minmag;
int v_mo_flags;
int v_tmp;
int v_mo_save_fsr0l;
int v_mo_save_fsr0h;
int v_mo_save_fsr1l;
int v_mo_save_fsr1h;
int v_mo_save_fsr2l;
int v_mo_save_fsr2h;

//int v_last_global;

int v_mo1_mo_angle;
int v_mo1_mo_maxmag;
int v_mo1_mo_minmag;
int v_mo1_mo_impl;
int v_mo1_mo_imph;
int v_mo1_mo_flags;

int v_mo2_next_mh;
int v_mo2_next_ml;
int v_mo2_next_mdh;
int v_mo2_next_mdl;
int v_mo2_bits;

#include "fmtb_pic.c"

//===========================================================================
// init_pic()
//===========================================================================
static void init_pic(void)
{
    picTest();

    chip = picChipCreate("WandPic", "Pic18lf2525");
    picChipSetCurrent(chip);

    //
    // globals (if any) go here
    //
    //picRegGlobal(v_last_global,0);
    
    //
    // These globals are defined in wand.asm
    //
    picRegGlobal(v_tmp,0);

    init_pic_globals();

    picRegSet(chip, TBLPTRU, 0);

    picSetCycle(0);
}

//===========================================================================
// picSpellWrite() - 
//===========================================================================
static void picSpellWrite(int val)
{
    if (!gCurrentSpell) return;
    ASSERT(val >=0 && val < 256);
    ASSERT(gCurrentMwanemeIndex < MAX_TOKENS);
    gCurrentSpell->pic_mwanemes[gCurrentMwanemeIndex++] = val;
}

//===========================================================================
// picSpellRead() - 
//===========================================================================
static int picSpellRead(void)
{
    static PSpell dummy;
    int val;
    if (!gCurrentSpell) return -1;
    if (gCurrentSpell->name == 0) {
        switch(gCurrentMwanemeIndex) {
            case 0:
                gCurrentMwanemeIndex++;
                return (PIC_MWN_END_SPELLS&0xff);
            case 1:
                gCurrentSpell = 0;
                gCurrentMwanemeIndex = 0;
                return (PIC_MWN_END_SPELLS>>8);
            default:
                ASSERT(0);
        }
    }
    ASSERT(gCurrentSpell);
    ASSERT(gCurrentMwanemeIndex < MAX_TOKENS);
    val = gCurrentSpell->pic_mwanemes[gCurrentMwanemeIndex++];
    if (DBG_LOADSPELL) {
        if (gCurrentMwanemeIndex-1<5) {
            static char *istr[8] = {
                "spellinfol",
                "spellinfoh",
                "maxscore",
                "holdoff",
                "initial octant",
                "???",
                "???",
                "???",
            };
            printf("%sREAD mwaneme[%3d] = %02x  %s%s\n",
                colorStr(2),
                gCurrentMwanemeIndex-1,
                val,
                istr[(gCurrentMwanemeIndex-1)&7],
                colorStr(-1));
        } else {
            printf("%sREAD mwaneme[%3d] = %02x  %s  dir=%2d oct=%d cnt=%d%s\n",
                colorStr(2),
                gCurrentMwanemeIndex-1,
                val,
                PIC_MWN_PULSE(val)?"pulse ":"circle",
                PIC_MWN_DIR(val),
                PIC_MWN_OCT(val),
                PIC_MWN_CNT(val),
                colorStr(-1));
        }
    }
    if (val == STOKEN_END && (gCurrentMwanemeIndex-1)>=6) {
        ASSERT(gCurrentMwanemeIndex-1 >= 6+2);  // at least two spell tokens
        gCurrentSpell++;
        gCurrentMwanemeIndex = 0;
        if (OPT_onespell) {
            gCurrentSpell = &dummy;
            dummy.name = 0;
        }
    }
    return val;
}


//===========================================================================
// spell_read_byte() - 
//===========================================================================
static void spell_read_byte(void)
{
    int val = picSpellRead();
    ASSERT(val >= 0);
    picRegSet(0,TABLAT,val);
}

//###########################################################################
//############################### CODE ######################################
//###########################################################################

//===========================================================================
// fmtbAssertFailed()
//===========================================================================
static void fmtbAssertFailed(
                const char *cond, 
                const char *func, 
                const char *file, 
                int line)
{
    fprintf(stderr,"  FAILED for Cmdline: %s\n",gCmdline);
}

//===========================================================================
// colorStr()
//===========================================================================
static char *colorStr(int bg)
{
    if (OPT_nocolor) return "";
    #define ESC "\033"
    switch(bg) {
        case 0:     return ESC "[40;37m";
        case 1:     return ESC "[0m" ESC "[41m";
        case 2:     return ESC "[0m" ESC "[42;30m";
        case 3:     return ESC "[0m" ESC "[43;30m";
        case 4:     return ESC "[0m" ESC "[44m";
        case 5:     return ESC "[0m" ESC "[45m";
        case 6:     return ESC "[0m" ESC "[46;30m";
        case 7:     return ESC "[0m" ESC "[47;30m";
        default:    return ESC "[0m";
    }
}


//===========================================================================
// breakpt()
//===========================================================================
static void breakpt(void)
{
}

//===========================================================================
// fatalError()
//===========================================================================
void fatalError(const char *fmt, ...)
{
    va_list ap;
    printf("\n\nERROR\n");
    fprintf(stderr,"\n\nERROR:\n");
    va_start(ap,fmt);
    vfprintf(stderr,fmt,ap);
    va_end(ap);
    fprintf(stderr,"\n\n");
    ASSERT(!"Fatal Error");
}

//===========================================================================
// setSilent() - set silent & return old value
//===========================================================================
static int setSilent(int val)
{
    int old = OPT_silent;
    OPT_silent = val;
    return old;
}

//===========================================================================
// eraseLocalVars() - erase all local variables
//===========================================================================
static void eraseLocalVars(void)
{
    //
    // erase all local vars
    //
#if 0
    picRegEraseRange(chip, v_last_global, 0x17f);
#else
    picRegEraseRange(chip, 0, 0x17f);
#endif

    //
    // erase special functions regs that are not considered global
    //
    picRegErase(chip, FSR0L);
    picRegErase(chip, FSR0H);
    picRegErase(chip, FSR1L);
    picRegErase(chip, FSR1H);
    picRegErase(chip, FSR2L);
    picRegErase(chip, FSR2H);

    picRegErase(chip, WREG);
    picRegErase(chip, PRODH);
    picRegErase(chip, PRODL);
    picRegErase(chip, TABLAT);
    picRegErase(chip, TBLPTRH);
    picRegErase(chip, TBLPTRL);

}

//===========================================================================
// marker()
//===========================================================================
static void marker(int n)
{
    int i;
    if (OPT_silent) return;
    for (i=0; i<20; i++) {
        printf("MARK %3d %4x %4x %4x %4x %4x %4x\n",n,i,i,i,i,i,i);
    }
}

//===========================================================================
// writeM1List()
//===========================================================================
static void writeM1List(Motion *mlist, int addr)
{
    Motion *m = mlist;

    //
    // Stored as follows:
    //
    //  Pause:
    //   0 - time (h)
    //   1 - time (l)
    //   2 - unused (set to 0xff)
    //   3 - angle (0xff indicates pause)
    //
    //  Angle:
    //   0 - time (h)
    //   1 - time (l)
    //   2 - max mag
    //   3 - angle (0-31)
    //   4 - imp (h)
    //   5 - imp (l)
    //   6 - min mag
    //   7 - flags
    //
    //  End of buffer 
    //   0 - time (h)
    //   1 - time (l)
    //   2 - unused (set to 0xff)
    //   3 - angle (0xfe indicates end of buffer)
    //
    while(1) {
        if (m->b_angle > 31) {
            ASSERT(m->b_angle == 0xff);
            if (m[1].b_angle == 0xfe) {
                break;
            }
            picRegSet(chip, addr++, (m->b_dtime>>8) & 0xff);
            picRegSet(chip, addr++, m->b_dtime & 0xff);
            picRegSet(chip, addr++, 0xff);
            picRegSet(chip, addr++, 0xff);

        } else {
            int flags = m->b_flags;
            flags = (flags & 1) | 0x80;

            picRegSet(chip, addr++, (m->b_dtime>>8) & 0xff);
            picRegSet(chip, addr++, m->b_dtime & 0xff);
            picRegSet(chip, addr++, m->b_maxmag);
            picRegSet(chip, addr++, m->b_angle);
            picRegSet(chip, addr++, (m->b_imp>>8) & 0xff);
            picRegSet(chip, addr++, m->b_imp & 0xff);
            picRegSet(chip, addr++, m->b_minmag);
            picRegSet(chip, addr++, flags);

        }

        m++;
    }

    picRegSet(chip, addr++, 0);
    picRegSet(chip, addr++, 0);
    picRegSet(chip, addr++, 0xff);
    picRegSet(chip, addr++, 0xfe);

    ASSERT(addr <= RAMBUF_ACC + RAMBUF_ACC_LEN);
}

//===========================================================================
// writeM2List()
//===========================================================================
static void writeM2List(Motion2 *m2list, int addr)
{
    Motion2 *m = m2list;
    int shape;
    int maxmag;
    int imp;
    int angle;
    int start = addr;

    ASSERT(m2list->angle == ANGLE_START);

    //
    // Stored as follows:
    //   0 - angle
    //   1 - impulsel
    //   2 - impulseh
    //   3 - maxmag
    //   4 - shape
    //
    while(1) {
        // w_shp_read
        shape = m->shape;
        maxmag = m->maxmag;
        imp = m->imp;
        angle = m->angle;
        m++;

        picRegSet(chip, addr++, angle);
        picRegSet32(chip, addr, addr+1, -1, -1, imp);
        addr += 2;
        picRegSet(chip, addr++, maxmag);
        picRegSet(chip, addr++, shape);

        if (angle & AFLG_END) break;
    }
    ASSERT(addr - start <= RAMBUF_M2LIST_LEN);
    ASSERT(addr - start <= RAMBUF_M3LIST_LEN);
}

//===========================================================================
// readM2List()
//===========================================================================
static void readM2List(Motion2 *m2list, int addr)
{
    Motion2 *md = m2list;
    int shape;
    int maxmag;
    int imp;
    int angle;
    int start = addr;

    //ASSERT(m2list->angle == ANGLE_START);

    while(1) {

        angle  = picRV(addr++);
        imp    = picRegValU32(chip, addr, addr+1, -1,-1);
        addr += 2;
        maxmag = picRV(addr++);
        shape  = picRV(addr++);

        // w_shp_write
        md->shape = shape;
        md->maxmag = maxmag;
        md->angle = angle;
        md->imp = imp;
        md++;

        if (angle & AFLG_END) break;
    }
    ASSERT(m2list->angle == ANGLE_START);
    ASSERT(addr - start <= RAMBUF_M2LIST_LEN);
    ASSERT(addr - start <= RAMBUF_M3LIST_LEN);
}

//===========================================================================
// readTokens()
//===========================================================================
static void readTokens(int *tokens, int addr)
{
    int start = addr;
    while(1) {
        int tok = picRV(addr++);
        *(tokens++) = tok;
        if (tok == MTOKEN_END) break;
    }
    ASSERT(addr - start <= RAMBUF_MTOKENS_LEN);
}

//===========================================================================
// adiff() - returned signed difference of 2 angles
//===========================================================================
static int adiff(int a1, int a0)
{
    int a = a1 - a0;
    a &= 0x1f;
    if (a & 0x10) a -= 0x20;
    return a;
}

//===========================================================================
// odiff() - returned signed difference of 2 octants
//===========================================================================
static int odiff(int o1, int o0)
{
    int o = o1 - o0;
    o &= 0x7;
    if (o & 0x4) o -= 0x8;
    return o;
}

//===========================================================================
// a2quad() - convert angle to quadrant
//===========================================================================
static int a2quad(int a)
{
    return ((a+4) >> 3) & 3;
}

//===========================================================================
// quad2a() - convert quadrant to angle
//===========================================================================
static int quad2a(int q)
{
    return (q<<3) & 0x1f;
}

//===========================================================================
// a2oct() - convert angle to octant
//===========================================================================
static int a2oct(int a)
{
    return ((a+2) >> 2) & 7;
}

//===========================================================================
// oct2a() - convert octant to angle
//===========================================================================
static int oct2a(int o)
{
    return (o<<2) & 0x1f;
}

//===========================================================================
// flagStr()
//===========================================================================
static const char *flagStr(int f)
{
    static char s[10];

    s[0] = f & MFLG_ZERO     ? 'Z' : ' ';
    s[1] = f & MFLG_MONO     ? 'M' : ' ';
    s[2] = f & MFLG_CIRCLE   ? 'C' : ' ';
    s[3] = f & MFLG_BEST_MAG ? 'G' : ' ';
    s[4] = f & MFLG_BEST_IMP ? 'I' : ' ';
    s[5] = 0;
    return s;
}

//===========================================================================
// spellTokenStr()
//===========================================================================
static char *spellTokenStr(int tok)
{
    static char buf[10];
    int oct = a2oct(TOKEN_ANGLE(tok));
    int dir = TOKEN_DIR(tok);
    if (tok==STOKEN_END) {
        return "  END";
    }
    switch(TOKEN_SHAPE(tok)) {
        case SSHP_PAUSE:   sprintf(buf,"Pause"); break;
        case SSHP_CIRCLE:  sprintf(buf," %3s%s",dir<0?" CW":"CCW",octStr2[oct]); break;
        case SSHP_OCIRCLE: sprintf(buf," %3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        case SSHP_OPULSE:  sprintf(buf,"  op%s",octStr2[oct]); break;
        case SSHP_PULSE:   sprintf(buf,"   P%s",octStr2[oct]); break;
        case SSHP_WILD:    sprintf(buf,"Wild "); break;
        default:           sprintf(buf,"?????"); break;
    }
    return buf;
}

//===========================================================================
// mwanemeDescStr()
//===========================================================================
static char *mwanemeDescStr(const char *mwanemes)
{
    static char buf[MAX_TOKENS*4];
    char *s = buf;
    const char *m = mwanemes;

    while(*m) {
        switch(*m) {
            case 'e': s += sprintf(s,"ccw2 "); break;
            case 'i': s += sprintf(s,"ccw3 "); break;
            case 'a': s += sprintf(s,"cw1 "); break;
            case 'o': s += sprintf(s,"cw2 "); break;
            case 'u': s += sprintf(s,"cw3 "); break;
            case 'y': s += sprintf(s,"ccw1 "); break;
            case 'd': s += sprintf(s,"^ "); break;
            case 'k': s += sprintf(s,"> "); break;
            case 'g': s += sprintf(s,"v "); break;
            case 'f': s += sprintf(s,"< "); break;
            case 'h': s += sprintf(s,"ur "); break;
            case 's': s += sprintf(s,"dr "); break;
            case 'm': s += sprintf(s,"dl "); break;
            case 'l': s += sprintf(s,"ul "); break;
            default:
                fatalError("ERROR: BAD PHONEME CHARACTER: %c\n    in '%s'\n",
                    *m,mwanemes);
        }
        ASSERT(s-buf < MAX_TOKENS*4 - 10);
        m++;
    }
    *s = 0;

    return buf;
}

//===========================================================================
// describeSpells()
//===========================================================================
static void describeSpells(PSpell *slist)
{
    static char *u = "------------------------------------------------------";
    PSpell *s;
    printf("Description of spells\n");
    printf("---------------------\n");
    printf("  %20s  %20s  %8s %4s %4s %4s  %s\n",
        "name",
        "mwanemes",
        "flags",
        "mxsc",
        "hold",
        "id",
        "mwaneme description");
    printf("  %.20s  %.20s  %.8s %.4s %.4s %.4s  %.19s\n",
        u,u,u,u,u,u,u);
        
    for (s=slist; s->name; s++) {
        printf("  %20s  %20s  %08x %4d %4d %4d  %s\n",
            s->name,
            s->mwanemes,
            s->flags,
            s->maxscore,
            s->holdoff,
            s->id,
            mwanemeDescStr(s->mwanemes));
        if (OPT_onespell) break;
    }
}

//===========================================================================
// motionTokenStr()
//===========================================================================
static char *motionTokenStr(unsigned int tok)
{
    static char buf[10];
    int oct = a2oct(TOKEN_ANGLE(tok));
    int dir = TOKEN_DIR(tok);
    if (tok==MTOKEN_END) {
        return "  END";
    }
    switch(TOKEN_SHAPE(tok)) {
        case MSHP_PAUSE:             sprintf(buf,"PAUSE"); break;
        case MSHP_CLEAN_CIRCLE:      sprintf(buf," %3s%s",dir<0?" CW":"CCW",octStr2[oct]); break;
        case MSHP_CLEAN_CIRCLE_PLUS: sprintf(buf,"+%3s%s",dir<0?" CW":"CCW",octStr2[oct]); break;
        case MSHP_DIRTY_CIRCLE:      sprintf(buf," %3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        case MSHP_DIRTY_CIRCLE_PLUS: sprintf(buf,"+%3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        case MSHP_PULSE:             sprintf(buf,"   p%s",octStr2[oct]); break;
        case MSHP_PULSE_PLUS:        sprintf(buf,"  +P%s",octStr2[oct]); break;
        case MSHP_CIRCLE_GAP:        sprintf(buf,"G%3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        default:                      sprintf(buf,"?????"); break;
    }
    return buf;
}

//===========================================================================
// lookPrint()
//===========================================================================
static void lookPrint(Motion *mlist, int mcnt)
{
    Motion *m, *mp;
    if (OPT_silent) return;
    mp = 0;
#if 0 && !MINIMAL
    for (m=mlist; m; mp=m, m = m->c_next) {
        if (m->b_angle == 0xff) {
            printf("AA 0x%08x: %04x Pause\n",
                m->b_atime,
                m->c_time
            );
        } else {
            printf("AA 0x%08x: %04x shape=%d %s a=%02x %c %d%s %s d=%2d imp=%04x %04x MaxDMag=%02x maxStep=%02x "
                "ad=%3d flg=%02x %s  %02x  %s\n",
                m->b_atime,
                m->c_time,
                m->c_shape,
                motionShapeStr[m->c_shape],
                m->b_angle,
                quadStr[a2quad(m->b_angle)][0],
                m->c_oct,
                octStr[m->c_oct],
                octStr2[m->c_oct],
                m->c_dir,
                m->c_imp,
                m->c_isum,
                m->c_maxdmag,
                m->c_maxstep,
                (m==mlist || m->b_angle == 0xff || mp->b_angle == 0xff) ? 
                        0:adiff(m->b_angle,mp->b_angle),
                m->b_flags,
                flagStr(m->b_flags),
                m->token,
                motionTokenStr(m->token));
        }
    }
    printf("\n\n");
#else
    for (m=mlist; m; mp=m, m = m->c_next) {
        if (m->b_angle == 0xff) {
            printf("AA2 0x%08x: %04x Pause\n",
                m->b_atime,
                m->b_dtime);
        } else {
            printf("AA2 0x%08x: %04x shape=%d %s a=%02x %c %d%s %s d=%2d imp=%04x %04x MaxDMag=%02x maxStep=%02x "
                "ad=%3d flg=%02x %s  %02x  %s\n",
                m->b_atime,
                m->b_dtime,
                0, //m->c_shape,
                motionShapeStr[m->c_shape],
                m->b_angle,
                quadStr[a2quad(m->b_angle)][0],
                a2oct(m->b_angle),
                octStr[a2oct(m->b_angle)],
                octStr2[a2oct(m->b_angle)],
                m->c_dir,
                m->c_imp,
                0, //m->c_isum,
                m->b_deltamag, //m->c_maxdmag,
                0, //m->c_maxstep,
                (m==mlist || m->b_angle == 0xff || mp->b_angle == 0xff) ? 
                        0:adiff(m->b_angle,mp->b_angle),
                m->b_flags,
                flagStr(m->b_flags),
                0, //m->token,
                motionTokenStr(m->token));
        }
    }
    printf("\n\n");
#endif
}

//===========================================================================
// stringMotion()
//===========================================================================
static char *stringMotion(char *str, Motion *mlist, Motion *m)
{
    static char strBuf[1000];
    char *s = strBuf;

    if (mlist && ((unsigned int)(m-mlist)<CNT_MAX)) {
        s+=sprintf(s,"%smlist[%3d] = %08x = ",str,m-mlist,
                OPT_noaddr?0:(int)m);
    } else {
        s+=sprintf(s,"%smlist[??] = %08x = ",str,
                OPT_noaddr?0:(int)m);
    }
    if (m->b_angle == 0xff) {
        s+=sprintf(s,"0x%08x: %04x Pause    a=%02x sh=%02x",
            m->b_atime,
            m->b_dtime,
            m->b_angle,
            m->c_shape & MSHF_SHP_MASK);
    } else {
        s+=sprintf(s,"0x%08x: %3s%s a=%02x %d %c imp=%04x mag=%02x sh=%02x: %s %s%s%s%s%s%s%s dmag=%02x %02x (%5.3f)"
            " flg=%02x %s",
            m->b_atime,
            (m->c_dir<0)?" cw":"ccw",
            m->b_flags & MFLG_MONO ? " ":"?",
            m->b_angle,
            a2oct(m->b_angle),
            octStr2[a2oct(m->b_angle)][0],
            m->c_imp ? m->c_imp : m->b_imp,
            m->b_maxmag,
            m->c_shape & MSHF_SHP_MASK, //m->c_shape,
            motionShapeStr[m->c_shape & MSHF_SHP_MASK],
            m->b_flags & MFLG_CIRCLE     ? "C" : " ",
            (m->c_prev && m->c_prev->c_shape==MSHP_PAUSE)? "P" : " ",
            m->b_flags & MFLG_MONO       ? "M" : " ",
            (m->c_dir<0)               ? "D" : " ",
            m->b_flags & MFLG_ZERO       ? "Z" : " ",
            m->b_flags & MFLG_BEST_MAG   ? "G" : " ",
            m->b_flags & MFLG_BEST_IMP   ? "I" : " ",
            m->b_maxmag-m->b_minmag,
            m->b_deltamag,
            (double)m->b_deltamag/(double)m->b_maxmag,
            m->b_flags,
            flagStr(m->b_flags));
    }
    return strBuf;
}

//===========================================================================
// stringMotion2()
//===========================================================================
static char *stringMotion2(char *str, Motion2 *mlist, Motion2 *m)
{
    static char strBufList[4][1000];
    static char *strBuf = 0;
    char *s;

    if (strBuf == strBufList[3]) {
        strBuf =  strBufList[0];
    } else if (strBuf == strBufList[0]) {
        strBuf = strBufList[1];
    } else if (strBuf == strBufList[1]) {
        strBuf = strBufList[2];
    } else {
        strBuf = strBufList[3];
    }


    s = strBuf;

    if (mlist) {
        s+=sprintf(s,"%smlist[%3d] = %08x = ",str,m-mlist,
                OPT_noaddr?0:(int)m);
    } else {
        s+=sprintf(s,"%smlist[??] = %08x = ",str,
                OPT_noaddr?0:(int)m);
    }

    if (m->angle & AFLG_START) {
        s+=sprintf(s,"START");
        return strBuf;
    }
    if (m->angle & AFLG_END) {
        s+=sprintf(s,"END");
        return strBuf;
    }

    s+=sprintf(s,"            %3s%s a=%02x %d %s imp=%04x mag=%02x sh=%02x: %s %s%s%s%s%s",
        (m->shape & MSHF_DIR)?" cw":"ccw",
        m->shape &  MSHF_MONO ? " ":"?",
        m->angle,
        a2oct(m->angle),
        octStr2[a2oct(m->angle)],
        m->imp,
        m->maxmag,
        m->shape & MSHF_SHP_MASK,
        motionShapeStr[m->shape & MSHF_SHP_MASK],
        m->shape & MSHF_CIRCLE     ? "C" : " ",
        m->shape & MSHF_PREV_PAUSE ? "P" : " ",
        m->shape & MSHF_MONO       ? "M" : " ",
        m->shape & MSHF_DIR        ? "D" : " ",
        "");
    return strBuf;
}

//===========================================================================
// printMotion()
//===========================================================================
static void printMotion(char *str, Motion *mlist, Motion *m, int color)
{
    if (OPT_silent) return;
    if (color >= 0 && color <= 7) {
        printf("%s",colorStr(color));
    }
    printf("%s",stringMotion(str,mlist,m));
    printf("%s\n",colorStr(-1));
}

//===========================================================================
// printMotion2()
//===========================================================================
static void printMotion2(char *str, Motion2 *mlist, Motion2 *m, int color)
{
    if (OPT_silent) return;
    if (color >= 0 && color <= 7) {
        printf("%s",colorStr(color));
    }
    printf("%s",stringMotion2(str,mlist,m));
    printf("%s\n",colorStr(-1));
}

//===========================================================================
// all3Print()
//===========================================================================
static void all3Print(Motion *mlist, Motion2 *m2list_min, Motion2 *m2list_pic)
{
    int show_orig = 1;
    int show_min  = 1;
    int show_pic  = 1;

    char *str1, *str2, *str3;
    char *s1, *s2, *s3;
    Motion *m = mlist;
    Motion2 *mm = m2list_min;
    Motion2 *mp = m2list_pic;
    Motion2 *ma;
    int waspausec = 0;
    int waspausep = 0;
    int skipcnt;

    if (OPT_silent) return;

    printMotion2("Mini ",m2list_min,mm,2);
    mm++;

    printMotion2("Pic  ",m2list_min,mp,4);
    mp++;

    while(1) {
        int err=0;

        if (mm && (mm->angle & AFLG_END)) mm = 0;
        if (mp && (mp->angle & AFLG_END)) mp = 0;
        if (!m && !mm && !mp) break;

        printf("\n");

        str1 = m ? stringMotion("Orig ",mlist,m) : "Orig END:";
        str2 = mm ? stringMotion2("Mini ",m2list_min,  mm) : "Mini END";
        str3 = mp ? stringMotion2("Pic  ",m2list_pic,mp) : "Pic  END";

        ma = mm ? mm : mp;

        if (!ma) {
            m = m ? m->c_next : 0;
        } else if (!m) {
            mm = mm ? mm+1 : mm;
            mp = mp ? mp+1 : mp;
        } else if ((ma->shape & MSHF_SHP_MASK) == MSHP_CIRCLE_GAP &&
                    m->c_shape != MSHP_CIRCLE_GAP) {

            str1 = "Orig --:";
            mm = mm ? mm+1 : mm;
            mp = mp ? mp+1 : mp;
            if (show_orig) {
                err=1;
            }
        } else if ((ma->shape & MSHF_SHP_MASK) != MSHP_CIRCLE_GAP &&
                    m->c_shape == MSHP_CIRCLE_GAP) {

            str2 = "Mini --:";
            str3 = "Pic  --:";
            m = m->c_next;
            if (show_min && show_pic) {
                err=1;
            }
        } else if (mm && (mm->shape & MSHF_PREV_PAUSE) && !waspausec &&
                   mp && (mp->shape & MSHF_PREV_PAUSE) && !waspausep) {
            waspausec = 1;
            waspausep = 1;
            str2 = "Mini PAUSE";
            str3 = "Pic  PAUSE";
            m = m->c_next;
        } else if (mm && (mm->shape & MSHF_PREV_PAUSE) && !waspausec) {
            waspausec = 1;
            waspausep = 0;
            str2 = "Mini PAUSE";
            mp = mp ? mp+1 : mp;
            m = m->c_next;
        } else if (mp && (mp->shape & MSHF_PREV_PAUSE) && !waspausep) {
            waspausep = 1;
            waspausec = 0;
            str3 = "Pic  PAUSE";
            mm = mm ? mm+1 : mm;
            m = m->c_next;
        } else {
            waspausec = 0;
            waspausep = 0;
            mm = mm ? mm+1 : mm;
            mp = mp ? mp+1 : mp;
            m = m->c_next;
        }

        do {
            int c1=0, c2=0, c3=0;
            int cnt = 0;
            skipcnt = 0;
            if (err) break;
            s1 = str1;
            s2 = str2;
            s3 = str3;
            while(1) {
                cnt++;
                if (*s1 == ':') c1++;
                if (*s2 == '=') c2++;
                if (*s3 == '=') c3++;
                if (c1) {
                    skipcnt = cnt;
                    break;
                }
                if (c2 == 2) {
                    skipcnt = cnt;
                    c2++;
                }
                if (c3 == 2) {
                    skipcnt = cnt;
                    c3++;
                }
                s1 = *s1 ? s1+1 : s1;
                s2 = *s2 ? s2+1 : s2;
                s3 = *s3 ? s3+1 : s3;
            }
        } while(0);

        if (show_orig) {
            int cnt = 0;
            s1 = str1;
            s2 = str2;
            s3 = str3;
            printf("%s",colorStr(5));
            while(cnt<skipcnt) {
                cnt++;
                printf("%c",*s1);
                s1 = *s1 ? s1+1 : s1;
                s2 = *s2 ? s2+1 : s2;
                s3 = *s3 ? s3+1 : s3;
            }
            printf("%c",*s1);
            s1 = *s1 ? s1+1 : s1;
            s2 = *s2 ? s2+1 : s2;
            s3 = *s3 ? s3+1 : s3;
            while(1) {
                if (*s1 == 0) break;
                if ((*s1 == *s2 || *s2==0) && !err) {
                    printf("%s",colorStr(5));
                } else {
                    printf("%s",colorStr(1));
                }
                printf("%c",*s1);
                s1 = *s1 ? s1+1 : s1;
                s2 = *s2 ? s2+1 : s2;
                s3 = *s3 ? s3+1 : s3;
            }
            printf("%s\n",colorStr(-1));
        }
        
        if (show_min) {
            int cnt = 0;
            s1 = str1;
            s2 = str2;
            s3 = str3;
            printf("%s",colorStr(2));
            while(cnt<skipcnt) {
                cnt++;
                printf("%c",*s2?*s2:' ');
                s1 = *s1 ? s1+1 : s1;
                s2 = *s2 ? s2+1 : s2;
                s3 = *s3 ? s3+1 : s3;
            }
            printf("%c",*s2?*s2:' ');
            s1 = *s1 ? s1+1 : s1;
            s2 = *s2 ? s2+1 : s2;
            s3 = *s3 ? s3+1 : s3;
            while(1) {
                int e = err;
                if (*s2 == 0) break;
                
                if (show_orig) {
                    if (*s1 != *s2 && *s1) e=1;
                }
                if (show_pic) {
                    if (*s3 != *s2 && *s3) e=1;
                }
                if (!e) {
                    printf("%s",colorStr(2));
                } else {
                    printf("%s",colorStr(1));
                }
                printf("%c",*s2);
                s1 = *s1 ? s1+1 : s1;
                s2 = *s2 ? s2+1 : s2;
                s3 = *s3 ? s3+1 : s3;
            }
            printf("%s\n",colorStr(-1));
        }
        
        if (show_pic) {
            int cnt = 0;
            s1 = str1;
            s2 = str2;
            s3 = str3;
            printf("%s",colorStr(4));
            while(cnt<skipcnt) {
                cnt++;
                printf("%c",*s3?*s3:' ');
                s1 = *s1 ? s1+1 : s1;
                s2 = *s2 ? s2+1 : s2;
                s3 = *s3 ? s3+1 : s3;
            }
            printf("%c",*s3?*s3:' ');
            s1 = *s1 ? s1+1 : s1;
            s2 = *s2 ? s2+1 : s2;
            s3 = *s3 ? s3+1 : s3;
            while(1) {
                if (*s3 == 0) break;
                if ((*s2 == *s3 || *s2==0) && !err) {
                    printf("%s",colorStr(4));
                } else {
                    printf("%s",colorStr(1));
                }
                printf("%c",*s3);
                s1 = *s1 ? s1+1 : s1;
                s2 = *s2 ? s2+1 : s2;
                s3 = *s3 ? s3+1 : s3;
            }
            printf("%s\n",colorStr(-1));
        }
        
    }
    printf("\n\n");
}

#if 0
//===========================================================================
// bothPrint()
//===========================================================================
static void bothPrint(Motion *mlist, Motion2 *m2list)
{
    char *str1, *str2;
    char *s1, *s2;
    Motion *m = mlist;
    Motion2 *m2 = m2list;
    int waspause = 0;

    if (OPT_silent) return;

    printMotion2("Mini ",m2list,m2,-1);
    m2++;

    while(1) {
        int err=0;

        if (!m && !m2) break;
        if (m2->angle & AFLG_END) m2 = 0;

        str1 = m ? stringMotion("Orig ",mlist,m) : "Orig END:";
        str2 = m2 ? stringMotion2("Mini ",m2list,m2) : "Mini END";

        if (!m2) {
            m = m->c_next;
        } else if (!m) {
            m2++;
        } else if ((m2->shape & MSHF_SHP_MASK) == MSHP_CIRCLE_GAP &&
                    m->c_shape != MSHP_CIRCLE_GAP) {

            str1 = "Orig --:";
            m2++;
            err=1;
        } else if ((m2->shape & MSHF_SHP_MASK) != MSHP_CIRCLE_GAP &&
                    m->c_shape == MSHP_CIRCLE_GAP) {

            str2 = "Mini --:";
            m = m->c_next;
            err=1;
        } else if ((m2->shape & MSHF_PREV_PAUSE) && !waspause) {
            waspause = 1;
            str2 = "Mini PAUSE";
            m = m->c_next;
        } else {
            waspause = 0;
            m2++;
            m = m->c_next;
        }

        s1  = str1;
        s2 = str2;
        printf("%s",colorStr(5));
        while(1) {
            if (*s1 == ':') break;
            printf("%c",*s1);
            s1 = *s1 ? s1+1 : s1;
            s2 = *s2 ? s2+1 : s2;
        }
        printf("%c",*s1);
        s1 = *s1 ? s1+1 : s1;
        s2 = *s2 ? s2+1 : s2;
        while(1) {
            if (*s1 == 0) break;
            if ((*s1 == *s2 || *s2==0) && !err) {
                printf("%s",colorStr(5));
            } else {
                printf("%s",colorStr(3));
            }
            printf("%c",*s1);
            s1 = *s1 ? s1+1 : s1;
            s2 = *s2 ? s2+1 : s2;
        }
        printf("%s\n",colorStr(-1));
        
        s1  = str1;
        s2 = str2;
        while(1) {
            if (*s1 == ':') break;
            printf("%c",*s2?*s2:' ');
            s1 = *s1 ? s1+1 : s1;
            s2 = *s2 ? s2+1 : s2;
        }
        printf("%c",*s2?*s2:' ');
        s1 = *s1 ? s1+1 : s1;
        s2 = *s2 ? s2+1 : s2;
        while(1) {
            if (*s2 == 0) break;
            if ((*s1 == *s2 || *s1==0) && !err) {
                printf("%s",colorStr(-1));
            } else {
                printf("%s",colorStr(3));
            }
            printf("%c",*s2);
            s1 = *s1 ? s1+1 : s1;
            s2 = *s2 ? s2+1 : s2;
        }
        printf("%s\n",colorStr(-1));
        
    }
    printf("\n\n");
}
#endif

//===========================================================================
// initialPrint() - print initial motion data
//===========================================================================
static void initialPrint(Motion *mlist)
{
    Motion *m;
    if (OPT_silent) return;

    for (m=mlist; m->b_angle != 0xfe; m++) {
        if (m->b_angle == 0xff) {
            printf("AA1 0x%08x: %04x Pause\n",
                m->b_atime,
                m->b_dtime);
        } else {
            printf("AA1 0x%08x: %04x a=%02x %c %c imp=%04x mag=%02x %02x %02x dmag=%02x %02x (%5.3f)"
                " flg=%02x %s\n",
                m->b_atime,
                m->b_dtime,
                m->b_angle,
                quadStr[a2quad(m->b_angle)][0],
                octStr2[a2oct(m->b_angle)][0],
                m->b_imp,
                m->b_minmag,
                0, //m->b_avgmag,
                m->b_maxmag,
                m->b_maxmag-m->b_minmag,
                m->b_deltamag,
                (double)m->b_deltamag/(double)m->b_maxmag,
                m->b_flags,
                flagStr(m->b_flags));
        }
    }
    printf("\n\n");
}

//===========================================================================
// parse1_minimal()
//===========================================================================
static void parse1_minimal(Motion *mlist, Motion2 *m2list)
{
    Motion *m;
    Motion2 *md;
    int maxstep = 0;
    int maxdmag = 0;
    int asum = 0;

    int shape;
    int maxmag;
    int imp;
    int angle;

    int shape_flags;

    int mo_imp;
    int mo_minmag;
    int mo_maxmag;
    int mo_angle;
    int mo_flags;

    int mo_dir;

    int mo_oct;
    int prev_mo_oct;
    int prev_mo_angle;
    int step;

    int was_just_pause;
    int restart;
    int got_angle;
    int got_dir;

    int dbg_got_mo_dir = 0;
    int dbg_atime = 0;
    int dbg_atimes = 0;
    int dbg = DBG_PARSE1_MINIMAL;

    m = mlist;
    md = m2list;

    if (!OPT_silent) {
        printf("\nstart parse1_minimal\n");
    }

    angle = ANGLE_START;

    // w_shp_write
    md->shape = shape;
    md->maxmag = maxmag;
    md->angle = angle;
    md->imp = imp;
    md->dbg_atime = 0;
    printMotion2("WRITE ",m2list,md,4);
    md++;

    was_just_pause = 1;
    got_angle = 0;
    got_dir = 0;
    mo_dir = 0;

    while(1) {
        prev_mo_angle = mo_angle;
        prev_mo_oct   = mo_oct;
        step = 0;

        // w_mo_read
        mo_imp = m->b_imp;
        mo_minmag = m->b_minmag;    // only used for deltamag
        mo_maxmag = m->b_maxmag;
        mo_angle = m->b_angle;
        mo_flags = m->b_flags;
        dbg_atime = m->b_atime;
        printMotion("READ  ",mlist,m,2);
        m++;

        mo_oct = a2oct(mo_angle);

        if (mo_angle & 0x80) {
            if (was_just_pause) {
                if (mo_angle == 0xfe) break;
                continue;
            }
            restart = 1;
            
        } else if (was_just_pause) {
            restart = 1;
            got_angle = 0;

        } else {

            if (mo_oct != prev_mo_oct) restart = 1;

            step = adiff(mo_angle, prev_mo_angle);
            mo_dir = 0;
            if (step < 0) {
                step = -step;
                mo_dir = MSHF_DIR;
            }
            dbg_got_mo_dir = 1;
        }
        if (dbg) {
            printf("  restart=%d  mo_dir=%02x shape_flags=%02x wjpause=%d\n",
                restart,mo_dir,shape_flags,was_just_pause);
        }

        if (restart) {
            if (got_angle) {
                asum += step;
                if (step > maxstep) {
                    maxstep = step;
                }

                shape = MSHP_PULSE;
                if (!got_dir) {
                    shape_flags &= ~MSHF_MONO;
                }
                if ((mo_dir ^ shape_flags) & MSHF_DIR) {
                    shape_flags &= ~MSHF_MONO;
                }
                if ((shape_flags & MSHF_MONO) == 0) {
                    // pulse
                } else if (shape_flags & MSHF_PREV_PAUSE) {
                    // pulse
                } else if (mo_angle & 0x80) {
                    // pulse
                } else if (asum < 4) {
                    // pulse
                } else if (maxstep>5) {
                    // pulse
                } else {
                    shape = MSHP_DIRTY_CIRCLE;
                    if (maxstep>3) {
                        // dirty circle
                    } else if (maxdmag >=8) {
                        // dirty circle
                    } else if (shape_flags & MSHF_ZERO) {
                        // dirty circle
                    } else {
                        shape = MSHP_CLEAN_CIRCLE;
                    }
                }

                if (dbg) {
                    printf("  shape=%02x  shape_flags=%02x mo_dir=%02x\n",
                        shape,shape_flags,mo_dir);
                }
                shape |= (shape_flags & MSHF_FLG_MASK);

                if ((shape & MSHF_MONO) == 0) {
                    shape &= ~MSHF_DIR;
                }

                // w_shp_write
                md->shape = shape;
                md->maxmag = maxmag;
                md->angle = angle;
                md->imp = imp;
                md->dbg_atime = dbg_atimes;
                printMotion2("WRITE ",m2list,md,4);
                md++;
            }

            if (mo_angle == 0xfe) break;
            if (dbg) {
                printf("  RESTART\n");
            }

            asum = 0;
            imp = 0;
            maxstep = 0;
            maxdmag = 0;
            maxmag = 0;
            got_angle = 0;
#if 0
            got_dir=0;
#endif

            shape_flags &= MSHF_DIR;
            shape_flags |= MSHF_MONO;

            restart = 0;

            dbg_atimes = dbg_atime;
        }

        if (mo_angle & 0x80) {
            was_just_pause = 1;
        } else {
            int dmag;

            if (dbg) {
                printf("  DIR: wjp=%d got_dir=%d got_angle=%d mo_dir=%02x "
                    "shape_flags=%02x mo_flags=%02x\n",
                        was_just_pause,got_dir,got_angle,
                        mo_dir,shape_flags,mo_flags);
            }
            if (was_just_pause) {
                was_just_pause = 0;
                got_dir = 0;
                dbg_got_mo_dir = 0;
                shape_flags |= MSHF_PREV_PAUSE;
                if (OPT_use_classic) {
                    shape_flags &= ~MSHF_MONO;
                }
            } else {

                ASSERT(dbg_got_mo_dir);
                if (!got_dir) {
                    shape_flags &= ~MSHF_DIR;
                    shape_flags |= mo_dir & MSHF_DIR;
                }

                if (dbg) {
                    printf("  DIR2: mo_dir=%02x shape_flags=%02x ^=%02x "
                        "MSHF_DIR=%02x  RES=%02x\n",
                            mo_dir,shape_flags,mo_dir ^ shape_flags,MSHF_DIR,
                            ((mo_dir ^ shape_flags) & MSHF_DIR));
                }
                if ((mo_dir ^ shape_flags) & MSHF_DIR) {
                    shape_flags &= ~(MSHF_MONO | MSHF_DIR);
                    shape_flags |= mo_dir & MSHF_DIR;
                }
                if (dbg) {
                    printf("  DIR2: shape_flags=%02x\n",shape_flags);
                }
                got_dir = 1;
            }
            got_angle = 1;

            imp += mo_imp;

            if (mo_maxmag > maxmag) {
                maxmag = mo_maxmag;
                angle  = mo_angle;
                dbg_atimes = dbg_atime;
            }

            ASSERT(MSHF_ZERO == MFLG_ZERO);
            shape_flags |= (mo_flags & MSHF_ZERO);
            dmag = mo_maxmag - mo_minmag;
            if (mo_flags & MSHF_ZERO) {
                dmag = mo_maxmag;
            }

            if (dmag > maxdmag) {
                maxdmag = dmag;
            }

            asum += step;

            if (maxstep < step) {
                maxstep = step;
            }
        }
    }

    angle = ANGLE_END;

    // w_shp_write
    md->shape = shape;
    md->maxmag = maxmag;
    md->angle = angle;
    md->imp = imp;
    md->dbg_atime = dbg_atimes;
    printMotion2("WRITE ",m2list,md,4);
    md++;

    printf("%s",colorStr(-1));

    if (!OPT_silent) {
        printf("end   parse1_minimal\n\n");
    }
}

//===========================================================================
// parse1_pic()
//===========================================================================
static void parse1_pic(Motion *mlist, Motion2 *m2list)
{
#if USE_PIC_PARSE1
    eraseLocalVars();
    writeM1List(mlist, RAMBUF_ACC);

    w_mo1_parse1();

    if (!OPT_silent || !USE_PIC_FILLGAP) {
        readM2List(m2list,  RAMBUF_M2LIST);
    }
#else
    parse1_minimal(mlist,m2list);
#endif
}

//===========================================================================
// parse1()
//===========================================================================
static void parse1(Motion *mlist, int mcnt)
{
    Motion *m;
    Motion *ms = 0;
    int maxstep = 0;
    int maxdmag = 0;
    int asum = 0;

#if 0
    if (MINIMAL) {
        parse1_minimal(mlist,cnt);
        return;
    }
#endif

    for (m=mlist; ; m++) {
        int o = a2oct(m->b_angle);
        int restart = 0;
        int ad = 0;
        int step = 0;
        int dir = 0;

        if (m->b_angle == 0xfe) {
            if (!OPT_use_classic) {
                if (m->c_prev) {
                    m->c_prev->c_next = 0;
                }
            }
            m = 0;
        }

        if (!m || !ms) {
            restart = 1;
        } else if (m->b_angle == 0xff) {
            restart = ms->b_angle != 0xff;
        } else if (ms->b_angle == 0xff) {
            restart = 1;
        } else {
            restart = o != a2oct(ms->b_angle);
            ad = adiff(m->b_angle, (m-1)->b_angle);
            step = ABS(ad);
            dir = ad < 0 ? -1 : 1;
        }

        if (step > maxstep) {
            maxstep = step;
        }

        if (restart) {
            if (ms) {
#if !MINIMAL
                ms->c_maxdmag = maxdmag;
#endif
                asum += step;
                if (dir != ms->c_dir) {
                    ms->b_flags &= ~MFLG_MONO;
                }
                if ((ms->b_flags & MFLG_MONO) == 0) {
                    asum = 0;
                    ms->c_dir = 0;
                }

                //
                // default token is pause
                //
                ms->token = TOKEN_CREATE_MO(MSHP_PAUSE,0,0);

                if (ms->b_angle & 0x80) {
                    ms->c_shape = MSHP_PAUSE;
                } else if (ms == mlist || !m || (ms-1)->b_angle == 0xff || m->b_angle==0xff) {
                    ms->c_shape = MSHP_PULSE;
                } else if ((ms->b_flags&MFLG_MONO) && maxstep<=5 && asum >= 4) {
                    ms->c_shape = MSHP_DIRTY_CIRCLE;
                    if (maxstep<=3 && maxdmag<8 && (ms->b_flags & MFLG_ZERO)==0) {
                        ms->c_shape = MSHP_CLEAN_CIRCLE;
                    }
                } else {
                    ms->c_shape = MSHP_PULSE;
                }
#if !MINIMAL
                ms->c_asum = asum;
#endif

                ms->c_next = m;
                if (m) m->c_prev = ms;
            }
            if (!m) break;
            ms = m;
            ms->c_dir = dir;
#if !MINIMAL
            ms->c_cnt = 1;
#endif
            ms->c_imp = ms->b_imp;
#if !MINIMAL
            ms->c_time = ms->b_dtime;
            ms->c_oct = o;
            ASSERT(ms->c_oct == a2oct(ms->b_angle));
            ms->c_maxstep = maxstep;
#endif
#if !MINIMAL
            ms->c_best_angle = ms->b_angle;
            ms->c_best_mag = ms->b_maxmag;
#endif
            ms->b_flags |= MFLG_MONO;

            maxdmag = ms->b_deltamag;
            asum = step;
            maxstep = step;
            ms->c_next = 0;
        } else {
#if !MINIMAL
            ms->c_cnt++;
            ms->c_time += m->b_dtime;
#endif
            if (m->b_angle == 0xff) {
                // double pause - ignore
            } else {
                if (dir != ms->c_dir) {
                    ms->b_flags &= ~MFLG_MONO;
                }
                ms->c_imp += m->b_imp;
#if MINIMAL
                if (m->b_maxmag > ms->b_maxmag) {
                    ms->b_angle  = m->b_angle;
                    ms->b_maxmag = m->b_maxmag;
                }
#else
                if (m->b_maxmag > ms->c_best_mag) {
                    ms->b_angle  = m->b_angle;
                    ms->b_maxmag = m->b_maxmag;
                    ms->c_best_angle = m->b_angle;
                    ms->c_best_mag   = m->b_maxmag;
                }
#endif
                ms->b_flags |= m->b_flags;
                maxdmag = MAX(maxdmag,m->b_deltamag);
                asum += step;
            }
        }
    }
}

//===========================================================================
// insertNode()
//===========================================================================
static Motion *insertNode(Motion *mlist, Motion *prev)
{
    // NOTE: MEMORY LEAK! these are never freed
    Motion *m = malloc(sizeof(Motion));
    memcpy(m,prev,sizeof(*m));
    m->c_prev = prev;
    m->c_next = prev->c_next;
    prev->c_next = m;
    if (prev->c_next) prev->c_next->c_prev = m;

    ASSERT(m->c_next && m->c_prev);

#if 0
    m->b_atime = m->c_next->b_atime;
    m->b_dtime = 0;
#else
    m->b_atime = (m->c_prev->b_atime + m->c_next->b_atime)/2;
    m->b_dtime = m->c_next->b_atime - m->b_atime;
    m->c_prev->b_dtime = m->b_atime - m->c_prev->b_atime;
#endif

    m->b_imp   = 0;
    m->b_minmag = 0;
    m->b_maxmag = 0;
    m->b_deltamag = 0;
    //m->b_angle;
    //m->b_flags;

    //m->c_shape;
#if !MINIMAL
    m->c_time = 0;
    m->c_maxstep = 0;
    m->c_cnt = 0;
    m->c_isum = 0;
#endif
    m->c_imp = 0;
    //m->c_dir;
    //m->c_best_angle;
    //m->c_best_mag;
    //m->c_maxdmag;
    //m->c_asum;

    //m->token;
    return m;
}

//===========================================================================
// fillCircleGaps_minimal()
//===========================================================================
static void fillCircleGaps_minimal(Motion2 *mlist)
{
    Motion2 m3list[1000];
    Motion2 *m, *mg, *md, *mgd, *ms, *msd;
    int cnt0, cnt1;
    int cnt1b;
    int oct, prev_oct, delta_oct;
    int gap_oct;

    int shape;
    int maxmag;
    int imp;
    int angle;

    int shape0;
    int added;
    int added_cnt;

    int dbg = DBG_FILLGAP_MINIMAL;
    int dbg_atime;
    int dbg_gap_atime;

    do {
        if (!OPT_silent) {
            printf("start fillCircleGaps_minimal\n");
        }

        m = mlist;
        md = m3list;
        while(1) {
            // w_shp_read
            shape = m->shape;
            maxmag = m->maxmag;
            imp = m->imp;
            angle = m->angle;
            dbg_atime = m->dbg_atime;
            printMotion2("READ  ",mlist,m,2);
            m++;

            // w_shp_write
            md->shape = shape;
            md->maxmag = maxmag;
            md->angle = angle;
            md->imp = imp;
            md->dbg_atime = dbg_atime;
            printMotion2("WRITE ",m3list,md,4);
            md++;

            if (angle & AFLG_END) break;
        }

        added = 0;
        added_cnt = 0;

        //
        // check for gaps in circles
        //
        ms  = &m3list[1];
        msd =  mlist+1;
        while(1) {

            m  = ms;
            md = msd;

            //breakpt();

            // w_shp_read
            shape = m->shape;
            maxmag = m->maxmag;
            imp = m->imp;
            angle = m->angle;
            dbg_atime = m->dbg_atime;
            printMotion2("READ  ",m3list,m,2);
            m++;

            if (angle & AFLG_END) break;

            ms = m;
            msd = md+1;

            shape0 = shape;
            cnt0 = cnt1 = 1-2;
            cnt1b = 0;
            oct = a2oct(angle);
            
            if (dbg) {
                printf("START: shape0=%02x oct=%d\n",shape0,oct);
            }
            while(1) {

                dbg_gap_atime = dbg_atime;

                // w_shp_write
                md->shape = shape;
                md->maxmag = maxmag;
                md->angle = angle;
                md->imp = imp;
                md->dbg_atime = dbg_atime;
                printMotion2("WRIT1 ",mlist,md,4);
                md++;


                prev_oct = oct;

                // w_shp_read
                shape = m->shape;
                maxmag = m->maxmag;
                imp = m->imp;
                angle = m->angle;
                dbg_atime = m->dbg_atime;
                printMotion2("READ  ",m3list,m,2);
                m++;

                if (angle & AFLG_END) break;
                if (shape & MSHF_PREV_PAUSE) break;

#if 1
                oct = a2oct(angle);
                delta_oct = oct - prev_oct;
                delta_oct &= 7;

                if ((shape0 & MSHF_MONO) == 0) {
                    shape0 = MSHF_MONO;
                    if (delta_oct & 4) {
                        shape0 |= MSHF_DIR;
                    }
                }
                
                if (shape0 & MSHF_DIR) delta_oct = -delta_oct;
                delta_oct &= 7;

                if (shape & MSHF_MONO) {
                    if ((shape^shape0) & MSHF_DIR) break;
                }
#else
                if ((shape0 & MSHF_MONO) == 0) {
                    shape0 = shape;
                }
                if (shape & MSHF_MONO) {
                    if ((shape^shape0) & MSHF_DIR) break;
                }

                oct = a2oct(angle);
                delta_oct = oct - prev_oct;
                if (shape0 & MSHF_DIR) delta_oct = -delta_oct;
                delta_oct &= 7;
#endif

                if (delta_oct != 1) break;
                if (dbg) {
                    printf("OK:     shape=%02x oct=%d delta_oct=%d\n",
                        shape,oct,delta_oct);
                }

                cnt0++;
            }

            if (dbg) {
                printf("NOT OK: shape=%02x oct=%d delta_oct=%d\n",
                    shape,oct,delta_oct);
            }

            if (angle & AFLG_END) break;

            m--;
            if (shape & MSHF_PREV_PAUSE) continue;
            if (shape & MSHF_MONO) {
                if ((shape^shape0) & MSHF_DIR) continue;
            }
            ASSERT(shape0 & MSHF_MONO);

            if (delta_oct != 2) continue;

            gap_oct = prev_oct+1;
            if (shape0 & MSHF_DIR) gap_oct -= 2;
            gap_oct &= 7;

            mg = m;
            mgd = md;
            m++;

            if (!OPT_silent || dbg) {
                printf("gap?   cnt0=%d cnt1=%d  oct=%d\n",cnt0+2,cnt1+2,oct);
            }
            dbg_gap_atime = (dbg_gap_atime + dbg_atime)/2;

            while(1) {

                // w_shp_write
                md->shape = shape;
                md->maxmag = maxmag;
                md->angle = angle;
                md->imp = imp;
                md->dbg_atime = dbg_atime;
                printMotion2("WRIT2 ",mlist,md,4);
                md++;

                prev_oct = oct;

                // w_shp_read
                shape = m->shape;
                maxmag = m->maxmag;
                imp = m->imp;
                angle = m->angle;
                dbg_atime = m->dbg_atime;
                printMotion2("READ  ",m3list,m,2);
                m++;

                if (angle & AFLG_END) break;
                if (shape & MSHF_PREV_PAUSE) break;

                if (shape & MSHF_MONO) {
                    if ((shape^shape0) & MSHF_DIR) break;
                }

                oct = a2oct(angle);
                delta_oct = oct - prev_oct;
                if (shape0 & MSHF_DIR) delta_oct = -delta_oct;
                delta_oct &= 7;

                if (delta_oct == 2) {
                    cnt1b++;
                    if (cnt1b > 1) break;
                } else if (delta_oct != 1) {
                    break;
                }

                if (dbg) {
                    printf("OK:     shape=%02x oct=%d delta_oct=%d\n",
                        shape,oct,delta_oct);
                }

                cnt1++;
            }

            m--;
            //breakpt();

            if (dbg) {
                printf("check: cnt0=%d cnt1=%d\n",cnt0+2,cnt1+2);
            }

            if (cnt0>=2-2 && cnt1>=2-2 && (cnt0+cnt1 >= 6-2-2)) {
                shape = MSHP_CIRCLE_GAP|MSHF_MONO;
                shape |= shape0 & MSHF_DIR;
                maxmag = 0;
                imp = 0;
                angle = oct2a(gap_oct);
                
                ms = mg;
                md = mgd;

                // w_shp_write
                md->shape = shape;
                md->maxmag = maxmag;
                md->angle = angle;
                md->imp = imp;
                md->dbg_atime = dbg_gap_atime;
                printMotion2("WRIT3 ",mlist,md,5);
                md++;

                msd = md;
                added=1;
                added_cnt++;
            }
        }

        angle = ANGLE_END;

        // w_shp_write
        md->shape = shape;
        md->maxmag = maxmag;
        md->angle = angle;
        md->imp = imp;
        md->dbg_atime = dbg_atime;
        printMotion2("WRIT4 ",mlist,md,4);
        md++;

        if (!OPT_silent) {
            printf("end   fillCircleGaps_minimal (added %d)\n",added_cnt);
        }
    } while(added);
    (void)dbg;
}


//===========================================================================
// fillCircleGaps_pic()
//===========================================================================
static void fillCircleGaps_pic(Motion2 *mlist)
{
#if USE_PIC_FILLGAP
    eraseLocalVars();
#if !USE_PIC_PARSE1
    writeM2List(mlist, RAMBUF_M2LIST);
#endif

    w_mo2_fill_circle_gaps();

    if (!OPT_silent || !USE_PIC_FINDWEAK) {
        readM2List(mlist,  RAMBUF_M2LIST);
    }
#else
    fillCircleGaps_minimal(mlist);
#endif
}

//===========================================================================
// fillCircleGaps()
//===========================================================================
static int fillCircleGaps(Motion *mlist, int mcnt)
{
    Motion *ms, *m, *mg;
    int cnt0, cnt1;
    int oct0, oct;
    int added = 0;

    if (!OPT_silent) {
        printf("start fillCircleGaps\n");
    }

    //
    // check counter-clockwise circles (dir == 1)
    //
    for (ms=mlist; ms; ) {
        printMotion("READ1 ",mlist,ms,2);
        if (ms->c_dir < 0) {
            ms = ms->c_next;
            continue;
        }
        cnt0 = 1;
#if !MINIMAL
        ASSERT(ms->c_oct == a2oct(ms->c_best_angle));
#endif
#if MINIMAL
        oct0 = a2oct(ms->b_angle);
#else
        oct0 = ms->c_oct;
#endif
        for (m=ms->c_next; m; m=m->c_next) {
            printMotion("READ2 ",mlist,m,2);
            if (m->c_dir < 0) {
                break;
            }
#if MINIMAL
            oct = a2oct(m->b_angle);
#else
            oct = m->c_oct;
#endif
            if (((oct - oct0)&7) != 1) break;
            cnt0++;
            oct0 = oct;
        }
    
        if (!m) break;
        if (m->c_dir < 0 || ((oct - oct0)&7) != 2) {
            ms = m;
            continue;
        }
        
        mg = m;
        cnt1 = 1;
        oct0 = oct;
        for (m=m->c_next; m; m=m->c_next) {
            printMotion("READ3 ",mlist,m,2);
            oct = a2oct(m->b_angle);
            if (((oct - oct0)&7) != 1) break;
            cnt1++;
            oct0 = oct;
        }

        if (cnt0>=2 && cnt1>=2 && (cnt0+cnt1 > 5)) {
            m = insertNode(mlist,mg->c_prev);
            m->c_dir = 1;
#if !MINIMAL
            m->c_maxstep = 0;
#endif
#if MINIMAL
            {
                int o = a2oct(mg->b_angle);
                o = (o-1) & 7;
                m->b_angle = oct2a(o);
            }
#else
            m->c_oct = (mg->c_oct - 1) & 7;
            m->c_best_angle = oct2a(m->c_oct);
            m->b_angle = m->c_best_angle;
#endif
            m->b_flags = MFLG_MONO | MFLG_MISSING_CIRCLE;
            m->c_shape = MSHP_CIRCLE_GAP;
            printMotion("INS4  ",mlist,m,5);
            added++;
        }
        ms = ms->c_next;
    }

    //
    // check clockwise circles (dir == -1)
    //
    for (ms=mlist; ms; ) {
        printMotion("READ5 ",mlist,ms,2);
        if (ms->c_dir > 0) {
            ms = ms->c_next;
            continue;
        }
        cnt0 = 1;
        oct0 = a2oct(ms->b_angle);
        for (m=ms->c_next; m; m=m->c_next) {
            printMotion("READ6 ",mlist,m,2);
            if (m->c_dir > 0) {
                break;
            }
            oct = a2oct(m->b_angle);
            if (((oct - oct0)&7) != 7) break;
            cnt0++;
            oct0 = oct;
        }
    
        if (!m) break;
        if (m->c_dir > 0 || ((oct - oct0)&7) != 6) {
            ms = m;
            continue;
        }
        
        mg = m;
        cnt1 = 1;
        oct0 = oct;
        for (m=m->c_next; m; m=m->c_next) {
            printMotion("READ7 ",mlist,m,2);
            oct = a2oct(m->b_angle);
            if (((oct - oct0)&7) != 7) break;
            cnt1++;
            oct0 = oct;
        }

        if (cnt0>=2 && cnt1>=2 && (cnt0+cnt1 > 5)) {
            m = insertNode(mlist,mg->c_prev);
            m->c_dir = -1;
#if !MINIMAL
            m->c_maxstep = 0;
#endif
#if MINIMAL
            {
                int o = a2oct(mg->b_angle);
                o = (o+1) & 7;
                m->b_angle = oct2a(o);
            }
#else
            m->c_oct = (mg->c_oct + 1) & 7;
            m->c_best_angle = oct2a(m->c_oct);
            m->b_angle = m->c_best_angle;
#endif
            m->b_flags = MFLG_MONO | MFLG_MISSING_CIRCLE;
            m->c_shape = MSHP_CIRCLE_GAP;
            printMotion("INS8  ",mlist,m,5);
            added++;
        }
        ms = ms->c_next;
    }
    if (!OPT_silent) {
        printf("end   fillCircleGaps (added %d)\n",added);
    }
    return added;
}

//===========================================================================
// findWeakCircles_minimal()
//===========================================================================
static void findWeakCircles_minimal(Motion2 *mlist)
{
    Motion2 *ms, *m, *m2;
    int dcnt, ccnt;
    int shape;
    int maxmag;
    int imp;
    int angle;
    int shp;


    if (!OPT_silent) {
        printf("start findWeakCircles_minimal\n");
    }

    m=mlist+1;
    while(1) {
        ms = m;


        ccnt = dcnt = 0;
        while(1) {
            // w_shp_read
            shape = m->shape;
            maxmag = m->maxmag;
            imp = m->imp;
            angle = m->angle;
            m++;

            if (angle & AFLG_END) break;
            if (shape & MSHF_PREV_PAUSE) break;

            shp = shape & MSHF_SHP_MASK;
            if (shp == MSHP_DIRTY_CIRCLE) {
                dcnt++;
            } else if (shp == MSHP_CLEAN_CIRCLE) {
                ccnt++;
#if ALLOW_GAPS_IN_STRONG_CIRCLES
            } else if (shp == MSHP_CIRCLE_GAP) {
#endif
            } else {
                break;
            }
        }

        m2 = m-1;
        m = ms;
        if (ccnt > 4 || (ccnt>0 && ccnt+dcnt>5)) {

            while(1) {
                m->shape |= MSHF_CIRCLE;
                m++;
                if (m == m2) break;
            }

        } else if (ccnt > 0) {

            while(1) {
                if ((m->shape & MSHF_SHP_MASK) == MSHP_CLEAN_CIRCLE) {
                    m->shape = (m->shape & MSHF_FLG_MASK) | MSHP_DIRTY_CIRCLE;
                }
                m++;
                if (m == m2) break;
            }

        } else {
            if (angle & AFLG_END) break;
            m++;
        }
    }
    if (!OPT_silent) {
        printf("end   findWeakCircles_minimal\n");
    }
}

//===========================================================================
// findWeakCircles_pic()
//===========================================================================
static void findWeakCircles_pic(Motion2 *mlist)
{
#if USE_PIC_FINDWEAK
    eraseLocalVars();
#if !USE_PIC_FILLGAP
    writeM2List(mlist, RAMBUF_M2LIST);
#endif

    w_mo3_find_weak_circles();

    if (!OPT_silent || !USE_PIC_FINDPULSE) {
        readM2List(mlist,  RAMBUF_M2LIST);
    }
#else
    findWeakCircles_minimal(mlist);
#endif
}

//===========================================================================
// findWeakCircles()
//===========================================================================
static void findWeakCircles(Motion *mlist, int mcnt)
{
    Motion *ms, *m, *m2;
    int dcnt, ccnt;

    if (!OPT_silent) {
        printf("start findWeakCircles\n");
    }
    for (ms=mlist; ms; ) {

        ccnt = dcnt = 0;
        for (m=ms; m; m=m->c_next) {
            if (m->c_shape == MSHP_DIRTY_CIRCLE) {
                dcnt++;
            } else if (m->c_shape == MSHP_CLEAN_CIRCLE) {
                ccnt++;
            } else if (OPT_use_classic) {
                break;
#if ALLOW_GAPS_IN_STRONG_CIRCLES
            } else if (m->c_shape == MSHP_CIRCLE_GAP) {
#endif
            } else {
                break;
            }
        }
        if (ccnt > 4 || (ccnt>0 && ccnt+dcnt>5)) {
            for (m2=ms; m2!=m; m2=m2->c_next) {
                m2->b_flags |= MFLG_CIRCLE;
            }
            m2 = ms->c_prev;
            if (m2->c_shape == MSHP_PULSE && (m2->b_flags & MFLG_MONO) && m2->c_dir == ms->c_dir) {
                int m2o = a2oct(m2->b_angle);
                int mso = a2oct(ms->b_angle);
                int d = odiff(m2o, mso);
                if (ABS(d) < 2) {
                    m2->b_flags |= MFLG_CIRCLE;
                }
            }
            m2 = m->c_prev;
            if (m->c_shape == MSHP_PULSE && (m->b_flags & MFLG_MONO) && m->c_dir == ms->c_dir) {
                int m2o = a2oct(m2->b_angle);
                int mo  = a2oct(m->b_angle);
                int d = odiff(mo, m2o);
                if (ABS(d) < 2) {
                    m->b_flags |= MFLG_CIRCLE;
                }
            }
        } else if (ccnt>0) {
            for (m2=ms; m2!=m; m2=m2->c_next) {
                if (OPT_use_classic) {
                    m2->c_shape = MSHP_DIRTY_CIRCLE;
                } else {
                    ASSERT(m2->c_shape != MSHP_CLEAN_CIRCLE_PLUS);
                    if (m2->c_shape == MSHP_CLEAN_CIRCLE) {
                        m2->c_shape  = MSHP_DIRTY_CIRCLE;
                    }
                }
            }
        }
        ms = m==ms ? m->c_next : m;
    }
    if (!OPT_silent) {
        printf("end   findWeakCircles\n");
    }
}

#if 0
//
// this version looks for short periods of time surrounded by gaps and
// adds PLUS bit to the best one
//
//===========================================================================
// findBestPulses_minimal()
//===========================================================================
static void findBestPulses_minimal(Motion2 *mlist)
{
    Motion2 *m, *ms, *save_m;
    Motion2 *best_imp_m=0, *best_mag_m=0;
    int best_imp;
    int best_mag;
    int d;
    int isum = 0;
    int mo, mpo;
    int prev_angle;

    int shape;
    int maxmag;
    int imp;
    int angle;
    int shp;

    int cur_angle;
    int min_angle;
    int max_angle;

    if (!OPT_silent) {
        printf("start findBestPulses_minimal\n");
    }
    m=mlist+1;
    while(1) {
        ms = m;
        best_imp_m = m;
        best_mag_m = m;

        // w_shp_read
        shape = m->shape;
        maxmag = m->maxmag;
        imp = m->imp;
        angle = m->angle;
        m++;

        if (angle&AFLG_END) break;
        shp = shape & MSHF_SHP_MASK;
        if (shp == MSHP_CIRCLE_GAP) continue;

        best_imp = imp;
        isum     = imp;
        best_mag = maxmag;
        
        cur_angle = min_angle = max_angle = 127;

        while(1) {
            prev_angle = angle;

            // w_shp_read
            shape = m->shape;
            maxmag = m->maxmag;
            imp = m->imp;
            angle = m->angle;
            m++;

            if (angle&AFLG_END) break;
            if (shape & MSHF_PREV_PAUSE) break;

            shp = shape & MSHF_SHP_MASK;

            if (shp == MSHP_CIRCLE_GAP) break;

            d = adiff(angle,prev_angle);

            if (shp != MSHP_CLEAN_CIRCLE && (shape&MSHF_CIRCLE)==0) {
                if (ABS(d) > 5) break;
            }

            cur_angle += d;
            min_angle = MIN(min_angle,cur_angle);
            max_angle = MAX(max_angle,cur_angle);

            mo = a2oct(angle);
            mpo = a2oct(prev_angle);
            d = odiff(mo, mpo);
            if (ABS(d) > 1) break;

            if (maxmag > best_mag) {
                best_mag = maxmag;
                best_mag_m = m-1;
            }
            isum += imp;
            if (imp > best_imp) {
                best_imp = imp;
                best_imp_m = m-1;
            }
        }
        save_m = m-1;
        
        if (!OPT_silent) {
            printf("consider PLUS isum=%04x   "
                "mag=m2list[%2d] (a%02x %02x %04x) "
                "imp=m2list[%2d] (a%02x %02x %04x) %d...%d  "
                "a=%d-%d=%d\n",
                isum,
                best_mag_m-mlist,
                best_mag_m->angle&0x1f,
                best_mag_m->maxmag,
                best_mag_m->imp,
                best_imp_m-mlist,
                best_imp_m->angle&0x1f,
                best_imp_m->maxmag,
                best_imp_m->imp,
                ms-mlist,
                save_m-mlist-1,
                max_angle,
                min_angle,
                max_angle - min_angle);
        }
        if (isum >= 0x500 && max_angle - min_angle < 20) {
            if (best_imp_m != best_mag_m) {
                if (best_imp_m->angle == best_mag_m->angle) {
                    best_imp_m = best_mag_m;
                    if (!OPT_silent) {
                        printf("  angle matches - use best_mag_m [%d]\n",
                            best_mag_m-mlist);
                    }
                } else {
                    if (!OPT_silent) {
                        printf("  reject: mag angle != imp angle\n");
                    }
                }
            }
            if (best_imp_m == best_mag_m) {
                m = best_imp_m;

                // w_shp_read
                shape = m->shape;
                maxmag = m->maxmag;
                imp = m->imp;
                angle = m->angle;
                m++;

                m--;

                m->shape |= MSHF_PLUS;

                if (!OPT_silent) {
                    printf("  set plus [%d]\n",
                        best_mag_m-mlist);
                }
            }
        }
        m = save_m;
    }
    if (!OPT_silent) {
        printf("end   findBestPulses_minimal\n");
    }
}
#else
#if 0
//
// This version has a bunch of stuff - not that great
//
#define BEST_PULSES_ALG_2           1
#define BEST_PULSES_ALLOW_CIRCLES   1
#define BEST_PULSES_ALG_3           1
#define BEST_PULSES_ALG_4           1
#define BEST_PULSES_ALG_5           1
#define BEST_PULSES_BEST_MI         1
#define BEST_PULSES_BETTER_ISUM     1

#if BEST_PULSES_ALLOW_CIRCLES && !BEST_PULSES_ALG_2
#error "Bad combo ALLOW_CIRCLES requires ALG_2"
#endif
#if BEST_PULSES_ALG_3 && !BEST_PULSES_ALG_2
#error "Bad combo ALG_3 requires ALG_2"
#endif
#if BEST_PULSES_ALG_4 && !BEST_PULSES_ALG_3
#error "Bad combo ALG_4 requires ALG_3"
#endif
#if BEST_PULSES_ALG_5 && !BEST_PULSES_ALG_3
#error "Bad combo ALG_5 requires ALG_3"
#endif
//===========================================================================
// findBestPulses_minimal()
//===========================================================================
static void findBestPulses_minimal(Motion2 *mlist)
{
    Motion2 *m, *ms;
    Motion2 *best_imp_m=0, *best_mag_m=0;
    int best_imp;
    int best_mag;
    int d;
    int isum = 0;
    int mo, mpo;
    int was_clean_circle=0;
    int prev_angle;

    int shape;
    int maxmag;
    int imp;
    int angle;
    int shp;

#if BEST_PULSES_ALG_3
    int save_angle;
#endif

    if (!OPT_silent) {
        printf("start findBestPulses_minimal\n");
    }
    m=mlist+1;
    while(1) {
        ms = m;
        best_imp_m = m;
        best_mag_m = m;

        // w_shp_read
        shape = m->shape;
        maxmag = m->maxmag;
        imp = m->imp;
        angle = m->angle;
        m++;

        if (angle&AFLG_END) break;
        shp = shape & MSHF_SHP_MASK;
#if BEST_PULSES_ALG_2
        if (shape & MSHF_PLUS) continue;
        if (shape & MSHF_NEAR_PLUS) continue;
        if (shp == MSHP_CIRCLE_GAP) continue;
#else
        if (shp == MSHP_DIRTY_CIRCLE) {
            // keep going
#if 1
            if (shape & MSHF_CIRCLE) {
                continue;
            }
#else
            if (was_clean_circle) {
                was_clean_circle = 0;
                continue;
            }
#endif
        } else if (shp == MSHP_PULSE) {
            // keep going
        } else if (shp == MSHP_CLEAN_CIRCLE) {
            was_clean_circle = 1;
            continue;
        } else {
            was_clean_circle = 0;
            continue;
        }
#endif
        was_clean_circle = 0;

        best_imp = imp;
        isum     = imp;
        best_mag = maxmag;
        
        while(1) {
            prev_angle = angle;

            // w_shp_read
            shape = m->shape;
            maxmag = m->maxmag;
            imp = m->imp;
            angle = m->angle;
            m++;

            if (angle&AFLG_END) break;
            if (shape & MSHF_PREV_PAUSE) break;

            shp = shape & MSHF_SHP_MASK;

#if BEST_PULSES_ALG_2
            if (shape & MSHF_PLUS) break;
            if (shape & MSHF_NEAR_PLUS) break;
            if (shp == MSHP_CIRCLE_GAP) break;
#else

            if (shp == MSHP_DIRTY_CIRCLE) {
                if (m->shape & MSHF_CIRCLE) {
                    break;
                }
            } else if (shp == MSHP_PULSE) {
                // keep going
            } else {
                break;
            }
#endif

            d = adiff(angle,prev_angle);
            if (ABS(d) > 5) break;

            mo = a2oct(angle);
            mpo = a2oct(prev_angle);
            d = odiff(mo, mpo);
            if (ABS(d) > 1) break;

            if (maxmag > best_mag) {
                best_mag = maxmag;
                best_mag_m = m-1;
            }
            isum += imp;
            if (imp > best_imp) {
                best_imp = imp;
                best_imp_m = m-1;
            }
        }
        if (isum >= 0x500) {
#if BEST_PULSES_BETTER_ISUM
            Motion2 *save_m = m;
#if BEST_PULSES_BEST_MI
            best_imp_m = best_mag_m;
#endif
            if (best_imp_m != best_mag_m) {
                if (!OPT_silent) {
                    printf("ignore   PLUS isum=%04x   "
                        "mag=m2list[%2d] (a%02x %02x %04x) "
                        "imp=m2list[%2d] (a%02x %02x %04x) %d...%d\n",
                        isum,
                        best_mag_m-mlist,
                        best_mag_m->angle&0x1f,
                        best_mag_m->maxmag,
                        best_mag_m->imp,
                        best_imp_m-mlist,
                        best_imp_m->angle&0x1f,
                        best_imp_m->maxmag,
                        best_imp_m->imp,
                        ms-mlist,
                        save_m-mlist-1);
                }
            }
            if (best_imp_m == best_mag_m) {
        
                m = best_mag_m;

                // w_shp_read
                shape = m->shape;
                maxmag = m->maxmag;
                imp = m->imp;
                angle = m->angle;
                m++;

#if BEST_PULSES_BEST_MI
                best_mag = maxmag;
#endif
                save_angle = angle;
                isum = imp;
                m--;

                if (!OPT_silent) {
                    printf("consider PLUS isum=%04x   "
                        "mag=m2list[%2d] (a%02x %02x %04x) "
                        "imp=m2list[%2d] (a%02x %02x %04x) %d...%d\n",
                        isum,
                        best_mag_m-mlist,
                        best_mag_m->angle&0x1f,
                        best_mag_m->maxmag,
                        best_mag_m->imp,
                        best_imp_m-mlist,
                        best_imp_m->angle&0x1f,
                        best_imp_m->maxmag,
                        best_imp_m->imp,
                        ms-mlist,
                        save_m-mlist-1);
                }

                //
                // add impulse from preceding motions
                //
                m--;
                while(1) {

                    // w_shp_read
                    shape = m->shape;
                    maxmag = m->maxmag;
                    imp = m->imp;
                    angle = m->angle;
                    m++;

                    if (angle & AFLG_START) break;
                    if (shape & MSHF_PLUS) break;
                    if (shape & MSHF_NEAR_PLUS) break;

                    d = adiff(angle,save_angle);
                    if (ABS(d) > 5) break;

#if BEST_PULSES_BEST_MI
                    if (maxmag > best_mag) {
                        best_mag = maxmag;
                        best_mag_m = m-1;
                    }
#endif

                    m -= 1;
                    isum += imp;
                    if (!OPT_silent) {
                        printf("          add isum=%04x   "
                            "  m=m2list[%2d] (a%02x %02x %04x)\n",
                            isum,
                            m-mlist,
                            angle&0x1f,
                            maxmag,
                            imp);
                    }

                    m -= 1;
                }

                //
                // set MSHF_NEAR_PLUS on following motions
                //
                m = best_mag_m;

                // w_shp_read
                shape = m->shape;
                maxmag = m->maxmag;
                imp = m->imp;
                angle = m->angle;
                m++;

                while(1) {

                    // w_shp_read
                    shape = m->shape;
                    maxmag = m->maxmag;
                    imp = m->imp;
                    angle = m->angle;
                    m++;

                    if (angle & AFLG_END) break;
                    if (shape & MSHF_PLUS) break;
                    if (shape & MSHF_NEAR_PLUS) break;

                    d = adiff(angle,save_angle);
                    if (ABS(d) > 5) break;

#if BEST_PULSES_BEST_MI
                    if (maxmag > best_mag) {
                        best_mag = maxmag;
                        best_mag_m = m-1;
                    }
#endif

                    m -= 1;
                    isum += imp;
                    if (!OPT_silent) {
                        printf("          add isum=%04x   "
                            "  m=m2list[%2d] (a%02x %02x %04x)\n",
                            isum,
                            m-mlist,
                            angle&0x1f,
                            maxmag,
                            imp);
                    }

                    m += 1;
                }
                m = save_m;

                
#if BEST_PULSES_BEST_MI
                if (best_mag_m != best_imp_m) {
                    if (best_mag_m->angle != best_imp_m->angle) {
                        isum = 0;
                        if (!OPT_silent) {
                            printf("  rej 1  PLUS isum=%04x   "
                                "mag=m2list[%2d] (a%02x %02x %04x) "
                                "imp=m2list[%2d] (a%02x %02x %04x) %d...%d\n",
                                isum,
                                best_mag_m-mlist,
                                best_mag_m->angle&0x1f,
                                best_mag_m->maxmag,
                                best_mag_m->imp,
                                best_imp_m-mlist,
                                best_imp_m->angle&0x1f,
                                best_imp_m->maxmag,
                                best_imp_m->imp,
                                ms-mlist,
                                save_m-mlist-1);
                        }
                    }

                }
#endif

                if (isum >= 0x500) {
                    if (!OPT_silent) {
                        printf("  accept PLUS isum=%04x   "
                            "mag=m2list[%2d] (a%02x %02x %04x) "
                            "imp=m2list[%2d] (a%02x %02x %04x) %d...%d\n",
                            isum,
                            best_mag_m-mlist,
                            best_mag_m->angle&0x1f,
                            best_mag_m->maxmag,
                            best_mag_m->imp,
                            best_imp_m-mlist,
                            best_imp_m->angle&0x1f,
                            best_imp_m->maxmag,
                            best_imp_m->imp,
                            ms-mlist,
                            save_m-mlist-1);
                    }
                    best_imp_m = best_mag_m;
                } else {
                    if (!OPT_silent) {
                        printf("  reject PLUS isum=%04x   "
                            "mag=m2list[%2d] (a%02x %02x %04x) "
                            "imp=m2list[%2d] (a%02x %02x %04x) %d...%d\n",
                            isum,
                            best_mag_m-mlist,
                            best_mag_m->angle&0x1f,
                            best_mag_m->maxmag,
                            best_mag_m->imp,
                            best_imp_m-mlist,
                            best_imp_m->angle&0x1f,
                            best_imp_m->maxmag,
                            best_imp_m->imp,
                            ms-mlist,
                            save_m-mlist-1);
                    }
                    //
                    // if this assert is triggered then consider using
                    // BEST_PULSES_BETTER_ISUM code.  Otherwise there is no 
                    // need.
                    //
                    ASSERT(!"BEST_PULSES_BETTER_ISUM is having an effect!!");
                    best_mag_m = 0;
                }
            }
#endif
            if (best_imp_m == best_mag_m) {
                m = best_imp_m;

                // w_shp_read
                shape = m->shape;
                maxmag = m->maxmag;
                imp = m->imp;
                angle = m->angle;
                m++;

                m--;

#if BEST_PULSES_ALG_2
                shp = shape & MSHF_SHP_MASK;
                if (shp == MSHP_CLEAN_CIRCLE &&
                    !BEST_PULSES_ALLOW_CIRCLES &&
                    (shape & MSHF_CIRCLE) != 0) {

                    m->shape |= MSHF_NEAR_PLUS;

                    if (!OPT_silent) {
                        printf("   set NEAR_PLUS in m2list[%2d] (1)\n", m-mlist);
                    }
                } else {
                    m->shape |= MSHF_PLUS;

                    if (!OPT_silent) {
                        printf("   set PLUS in m2list[%2d]\n", m-mlist);
                    }

#if BEST_PULSES_ALG_3
                    save_angle = angle;
#endif

                    //
                    // set MSHF_NEAR_PLUS on preceding motions
                    //
                    m--;
                    while(1) {
                        prev_angle = angle;

                        // w_shp_read
                        shape = m->shape;
                        maxmag = m->maxmag;
                        imp = m->imp;
                        angle = m->angle;
                        m++;

                        if (angle & AFLG_START) break;
                        if (shape & MSHF_PLUS) break;
                        if (shape & MSHF_NEAR_PLUS) break;

#if !BEST_PULSES_ALG_5
#if !BEST_PULSES_ALG_4
                        d = adiff(angle,prev_angle);
                        if (ABS(d) > 5) break;
#endif

                        mo = a2oct(angle);
                        mpo = a2oct(prev_angle);
                        d = odiff(mo, mpo);
                        if (ABS(d) > 1) break;
#endif
                        
#if BEST_PULSES_ALG_3
                        d = adiff(angle,save_angle);
                        if (ABS(d) >= 8) break;
#endif
                        m -= 1;
                        m->shape |= MSHF_NEAR_PLUS;
                        if (!OPT_silent) {
                            printf("   set NEAR_PLUS in m2list[%2d] (2)\n",
                                    m-mlist);
                        }
                        m -= 1;
                    }

                    //
                    // set MSHF_NEAR_PLUS on following motions
                    //
                    m = best_imp_m;

                    // w_shp_read
                    shape = m->shape;
                    maxmag = m->maxmag;
                    imp = m->imp;
                    angle = m->angle;
                    m++;

                    while(1) {
                        prev_angle = angle;

                        // w_shp_read
                        shape = m->shape;
                        maxmag = m->maxmag;
                        imp = m->imp;
                        angle = m->angle;
                        m++;

                        if (angle & AFLG_END) break;
                        if (shape & MSHF_PLUS) break;
                        if (shape & MSHF_NEAR_PLUS) break;

#if !BEST_PULSES_ALG_5
#if !BEST_PULSES_ALG_4
                        d = adiff(angle,prev_angle);
                        if (ABS(d) > 5) break;
#endif

                        mo = a2oct(angle);
                        mpo = a2oct(prev_angle);
                        d = odiff(mo, mpo);
                        if (ABS(d) > 1) break;
#endif
                        
#if BEST_PULSES_ALG_3
                        d = adiff(angle,save_angle);
                        if (ABS(d) >= 8) break;
#endif
                        m -= 1;
                        m->shape |= MSHF_NEAR_PLUS;
                        if (!OPT_silent) {
                            printf("   set NEAR_PLUS in m2list[%2d] (3)\n",
                                    m-mlist);
                        }
                        m += 1;
                    }

                }
#if 1
                m = ms;
#else
                m = ms+1;
#endif
#else
                m->shape |= MSHF_PLUS;
#endif
            }
        }
        m--;
    }
    if (!OPT_silent) {
        printf("end   findBestPulses_minimal\n");
    }
}
#else
//
// This version should be identical to original
//
//===========================================================================
// findBestPulses_minimal()
//===========================================================================
static void findBestPulses_minimal(Motion2 *mlist)
{
    Motion2 *m, *ms;
    Motion2 *best_imp_m, *best_mag_m;
    int best_imp, best_mag;
    int d;
    int isum = 0;
    int mo, mpo;

    int shp;
    int prev_shp;

    int shape;
    int maxmag;
    int imp;
    int angle;

    int prev_angle;


    if (!OPT_silent) {
        printf("start findBestPulses\n");
    }

    shp = -1;
    m = mlist + 1;

    while(1) {

        ms = m;

        // w_shp_read
        shape = m->shape;
        maxmag = m->maxmag;
        imp = m->imp;
        angle = m->angle;
        m++;

        if (angle & AFLG_END) break;

        prev_shp = shp;
        shp = shape & MSHF_SHP_MASK;
        if (shp == MSHP_DIRTY_CIRCLE) {
            if (prev_shp == MSHP_CLEAN_CIRCLE) continue;
        } else if (shp != MSHP_PULSE) {
            continue;
        }
        
        best_imp_m = ms;
        best_mag_m = ms;
        best_mag = maxmag;
        best_imp = imp;
        isum = imp;

        while(1) {

            prev_angle = angle;

            ms = m;

            // w_shp_read
            shape = m->shape;
            maxmag = m->maxmag;
            imp = m->imp;
            angle = m->angle;
            m++;

            if (angle & AFLG_END) break;

            if (shape & MSHF_PREV_PAUSE) {
                break;
            }

            shp = shape & MSHF_SHP_MASK;
            if (shp == MSHP_DIRTY_CIRCLE) {
                if (shape & MSHF_CIRCLE) break;
            } else if (shp != MSHP_PULSE) {
                break;
            }
            
            d = adiff(angle,prev_angle);
            if (ABS(d) > 5) break;

            mo = a2oct(angle);
            mpo = a2oct(prev_angle);
            d = odiff(mo, mpo);
            if (ABS(d) > 1) break;

            if (maxmag > best_mag) {
                best_mag = maxmag;
                best_mag_m = ms;
            }
            if (imp > best_imp) {
                best_imp = imp;
                best_imp_m = ms;
            }
            isum += imp;
        }

        if (isum >= 0x500) {
            if (best_imp_m == best_mag_m) {
                ASSERT((best_imp_m->shape & MSHF_SHP_MASK) == MSHP_PULSE ||
                   (best_imp_m->shape & MSHF_SHP_MASK) == MSHP_DIRTY_CIRCLE);
                
                m = best_imp_m;
                m->shape |= MSHF_PLUS;
            }
        }

        m = ms;
    }
    if (!OPT_silent) {
        printf("end   findBestPulses\n");
    }
}
#endif
#endif


//===========================================================================
// findBestPulses_pic()
//===========================================================================
static void findBestPulses_pic(Motion2 *mlist)
{
#if USE_PIC_FINDPULSE
    eraseLocalVars();
#if !USE_PIC_FINDWEAK
    writeM2List(mlist, RAMBUF_M2LIST);
#endif

    w_mo4_find_best_pulses();

    if (!OPT_silent || !USE_PIC_MARKLAST) {
        readM2List(mlist,  RAMBUF_M2LIST);
    }
#else
    findBestPulses_minimal(mlist);
#endif
}

//===========================================================================
// findBestPulses()
//===========================================================================
static void findBestPulses(Motion *mlist, int mcnt)
{
    Motion *m, *ms;
    Motion *best_imp=0, *best_mag=0;
    int d;
    int isum = 0;
    int mo, mpo;

    if (!OPT_silent) {
        printf("start findBestPulses\n");
    }
    for (ms=mlist; ms; ms = ms->c_next) {
        if (ms->c_shape == MSHP_DIRTY_CIRCLE && ms->c_prev->c_shape == MSHP_CLEAN_CIRCLE) continue;
        if (ms->c_shape != MSHP_PULSE && ms->c_shape != MSHP_DIRTY_CIRCLE) continue;
        best_imp = ms;
        best_mag = ms;
        isum = ms->c_imp;
        for (m=ms->c_next; m; ms=m, m=m->c_next) {
            if (m->c_shape != MSHP_PULSE && m->c_shape != MSHP_DIRTY_CIRCLE) break;
            if (m->c_shape == MSHP_DIRTY_CIRCLE && (m->b_flags & MFLG_CIRCLE)) break;

            d = adiff(m->b_angle,m->c_prev->b_angle);
            if (ABS(d) > 5) break;

            mo = a2oct(m->b_angle);
            mpo = a2oct(m->c_prev->b_angle);
            d = odiff(mo, mpo);
            if (ABS(d) > 1) break;

            if (m->b_maxmag > best_mag->b_maxmag) {
                best_mag = m;
            }
            if (m->c_imp > best_imp->c_imp) {
                best_imp = m;
            }
            isum += m->c_imp;
        }
        if (isum >= 0x500) {
            best_imp->b_flags |= MFLG_BEST_IMP;
            best_mag->b_flags |= MFLG_BEST_MAG;
            if (best_imp == best_mag) {
                switch(best_imp->c_shape) {
                    case MSHP_PULSE:
                        best_imp->c_shape = MSHP_PULSE_PLUS;
                        break;
                    case MSHP_DIRTY_CIRCLE:
                        best_imp->c_shape = MSHP_DIRTY_CIRCLE_PLUS;
                        break;
                    case MSHP_CLEAN_CIRCLE:
                        best_imp->c_shape = MSHP_CLEAN_CIRCLE_PLUS;
                        break;
                }
            }
        }
    }
    if (!OPT_silent) {
        printf("end   findBestPulses\n");
    }
}


//===========================================================================
// markLast_minimal() - mark last few(3) pulses as unimportant
//===========================================================================
static void markLast_minimal(Motion2 *mlist)
{
    Motion2 *m;
    int cnt = 3;
    int shape;
    int maxmag;
    int imp;
    int angle;

    m=mlist+1;
    while(1) {
        // w_shp_read
        shape = m->shape;
        maxmag = m->maxmag;
        imp = m->imp;
        angle = m->angle;
        m++;

        if (angle & AFLG_END) break;
    }
    m--;
    m--;

    shape = 0;
    do {
#if 0
        if (shape & MSHF_PREV_PAUSE) {
            cnt--;
            if (cnt == 0) break;
        }
#endif

        // w_shp_read
        shape = m->shape;
        maxmag = m->maxmag;
        imp = m->imp;
        angle = m->angle;
        m++;

        if (angle & AFLG_START) break;
        m--;
        m->shape &= ~MSHF_PLUS;

        printMotion2("marklast ",mlist,m,5);
        m--;
    } while(--cnt);
}

//===========================================================================
// markLast_pic() - mark last few(3) pulses as unimportant
//===========================================================================
static void markLast_pic(Motion2 *mlist)
{
#if USE_PIC_MARKLAST
    eraseLocalVars();
#if !USE_PIC_FINDPULSE
    writeM2List(mlist, RAMBUF_M2LIST);
#endif

    w_mo5_marklast();

    //
    // have to do this to get mtimes for pic
    //
    if (1 || !OPT_silent || !USE_PIC_TOKENIZE) {
        readM2List(mlist,  RAMBUF_M2LIST);
    }
#else
    markLast_minimal(mlist);
#endif
}

//===========================================================================
// markLast() - mark last few(3) pulses as unimportant
//===========================================================================
static void markLast(Motion *mlist, int mcnt)
{
    Motion *m;
    int cnt = 0;

    if (!mlist) return;
    for (m=mlist; m->c_next; m=m->c_next);
    while(m && cnt<3) {
        switch(m->c_shape) {
            case MSHP_PAUSE:
                break;
            case MSHP_CLEAN_CIRCLE:
            case MSHP_DIRTY_CIRCLE:
            case MSHP_PULSE:
            case MSHP_CIRCLE_GAP:
                cnt++;
                break;

            case MSHP_CLEAN_CIRCLE_PLUS:
                m->c_shape = MSHP_CLEAN_CIRCLE;
                cnt++;
                break;
            case MSHP_DIRTY_CIRCLE_PLUS:
                m->c_shape = MSHP_DIRTY_CIRCLE;
                cnt++;
                break;
            case MSHP_PULSE_PLUS:
                m->c_shape = MSHP_PULSE;
                cnt++;
                break;
        }
        m = m->c_prev;
    }
}

//===========================================================================
// tokenize_minimal()
//===========================================================================
static void tokenize_minimal(Motion2 *mlist, int *tokens, int *atimes)
{
    Motion2 *m;
    int shape;
    int maxmag;
    int imp;
    int angle;
    int dir;

    int atime;
    int cnt = 0;

    m=mlist+1;
    while(1) {
        // w_shp_read
        shape = m->shape;
        maxmag = m->maxmag;
        imp = m->imp;
        angle = m->angle;
        atime = m->dbg_atime;
        m++;
        if (angle & AFLG_END) break;

        dir = shape&MSHF_DIR?-1:1;
        if (((shape & MSHF_SHP_MASK) & ~MSHF_PLUS) == MSHP_PULSE) dir = 1;

        *(tokens++) = TOKEN_CREATE_MO(shape, dir, angle);
        *(atimes++) = atime;
        cnt++;
        ASSERT(cnt < 256);
    }
        
    *tokens = MTOKEN_END;
}

//===========================================================================
// tokenize_pic()
//===========================================================================
static void tokenize_pic(Motion2 *mlist, int *tokens, int *atimes)
{
#if USE_PIC_TOKENIZE
    int dummy[CNT_MAX];

    if (mlist && atimes) {
        //
        // just to get atimes
        //
        tokenize_minimal(mlist,dummy, atimes);
    }

    eraseLocalVars();
#if !USE_PIC_MARKLAST
    writeM2List(mlist, RAMBUF_M2LIST);
#endif

    w_mo6_tokenize();

    if (mlist) {
        readM2List(mlist,  RAMBUF_M2LIST);
    }
    readTokens(tokens, RAMBUF_MTOKENS);
#else
    tokenize_minimal(mlist,tokens,atimes);
#endif
}

//===========================================================================
// tokenize()
//===========================================================================
static void tokenize(Motion *mlist, int mcnt)
{
    Motion *m;
    for (m=mlist; m; m=m->c_next) {
#if !MINIMAL
        ASSERT(odiff(m->c_oct, a2oct(m->b_angle)) == 0);
#endif
        m->token = TOKEN_CREATE_MO(m->c_shape, m->c_dir, m->b_angle);
        ASSERT(m->token != MTOKEN_END);
    }
}

//===========================================================================
// look()
//===========================================================================
static void look(Motion *mlist, int mcnt)
{
    if (!OPT_silent) {
        printf("\n\n");
    }

    marker(1);
    parse1(mlist,mcnt);
    marker(2);

    if (!OPT_silent) {
        printf("\nPOST parse1\n\n");
        lookPrint(mlist,mcnt);
    }

    marker(3);
    while(fillCircleGaps(mlist,mcnt));
    marker(4);

    if (!OPT_silent) {
        printf("\nPOST fillCircleGaps\n\n");
        lookPrint(mlist,mcnt);
    }

    marker(5);
    findWeakCircles(mlist,mcnt);
    marker(6);

    if (!OPT_silent) {
        printf("\nPOST findWeakCircles\n\n");
        lookPrint(mlist,mcnt);
    }

    marker(7);
    findBestPulses(mlist,mcnt);
    marker(8);

    if (!OPT_silent) {
        printf("\nPOST findBestPulses\n\n");
        lookPrint(mlist,mcnt);
    }

    marker(9);
    markLast(mlist,mcnt);
    marker(10);

    if (!OPT_silent) {
        printf("\nPOST markLast\n\n");
        lookPrint(mlist,mcnt);
    }

    marker(11);
    tokenize(mlist,mcnt);
    marker(12);

    if (!OPT_silent) {
        printf("\nPOST tokenize\n\n");
        lookPrint(mlist,mcnt);
    }
}

//===========================================================================
// look_minimal()
//===========================================================================
static void look_minimal(Motion *mlist, int *tokens, int *atimes)
{
    Motion2 m2list[1000];

    marker(1);
    parse1_minimal(mlist,m2list);
    marker(2);


    if (!OPT_silent) {
        printf("\nPOST parse1\n\n");
        lookPrint(mlist,0);
    }


    marker(3);
    fillCircleGaps_minimal(m2list);
    marker(4);

    marker(5);
    findWeakCircles_minimal(m2list);
    marker(6);

    marker(7);
    findBestPulses_minimal(m2list);
    marker(8);


    marker(9);
    markLast_minimal(m2list);
    marker(10);

    marker(11);
    tokenize_minimal(m2list,tokens, atimes);
    marker(12);
}

//===========================================================================
// look_pic()
//===========================================================================
static void look_pic(Motion *mlist, int *tokens, int *atimes, PSpell *slist)
{

    gCurrentSpell = slist;
    gCurrentMwanemeIndex = 0;
    

    eraseLocalVars();
    writeM1List(mlist, RAMBUF_ACC);

    if (OPT_use_picsim) {

        w_check_motion_spell();
        
    } else {

        w_mo1_parse1();
        w_mo2_fill_circle_gaps();
        w_mo3_find_weak_circles();
        w_mo4_find_best_pulses();
        w_mo5_marklast();
        w_mo6_tokenize();

    }
    readTokens(tokens, RAMBUF_MTOKENS);
}


//===========================================================================
// look_all3()
//===========================================================================
static void look_all3(
                Motion *mlist,
                int mcnt, 
                int *tokens_min, 
                int *tokens_pic, 
                int *atimes_min, 
                int *atimes_pic)
{
    int i;
    Motion mlist_save[CNT_MAX];
    Motion2 m2list_min[1000];
    Motion2 m2list_pic[1000];

    memcpy(mlist_save,mlist,sizeof(mlist_save));



    if (!OPT_silent) {
        printf("\n\n");
    }

    parse1_pic(mlist,m2list_pic);

    for (i=0;i<CNT_MAX;i++) {
        if (memcmp(&mlist[i],&mlist_save[i],sizeof(Motion))) {
            printf("parse1_pic changed mlist[%d] = %08x\n",
                i,(int)&mlist[i]);
            ASSERT(!"parse1_pic changed mlist!!");
        }
    }

    marker(1);
    parse1_minimal(mlist,m2list_min);

    for (i=0;i<CNT_MAX;i++) {
        if (memcmp(&mlist[i],&mlist_save[i],sizeof(Motion))) {
            printf("parse1_minimal changed mlist[%d] = %08x\n",
                i,(int)&mlist[i]);
            ASSERT(!"parse1_minimal changed mlist!!");
        }
    }

    if (!OPT_silent) {
        printf("\n\n");
    }

    parse1(mlist,mcnt);
    marker(2);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST parse1\n\n");
        all3Print(mlist, m2list_min, m2list_pic);
    }

    //breakpt();

    marker(3);
    while(fillCircleGaps(mlist,mcnt));
    fillCircleGaps_minimal(m2list_min);
    fillCircleGaps_pic(m2list_pic);
    marker(4);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST fillCircleGaps\n\n");
        all3Print(mlist, m2list_min, m2list_pic);
    }

    //breakpt();

    marker(5);
    findWeakCircles(mlist,mcnt);
    findWeakCircles_minimal(m2list_min);
    findWeakCircles_pic(m2list_pic);
    marker(6);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST findWeakCircles\n\n");
        all3Print(mlist, m2list_min, m2list_pic);
    }

    //breakpt();

    marker(7);
    findBestPulses(mlist,mcnt);
    findBestPulses_minimal(m2list_min);
    findBestPulses_pic(m2list_pic);
    marker(8);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST findBestPulse\n\n");
        all3Print(mlist, m2list_min, m2list_pic);
    }

    //breakpt();

    marker(9);
    markLast(mlist,mcnt);
    markLast_minimal(m2list_min);
    markLast_pic(m2list_pic);
    marker(10);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST markLast\n\n");
        all3Print(mlist, m2list_min, m2list_pic);
    }

    //breakpt();

    marker(11);
    tokenize(mlist,mcnt);
    tokenize_minimal(m2list_min,tokens_min, atimes_min);
    tokenize_pic(m2list_pic,tokens_pic, atimes_pic);
    marker(12);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST tokenize\n\n");
        all3Print(mlist, m2list_min, m2list_pic);
    }

    //breakpt();
}

#if 0
//===========================================================================
// look_both()
//===========================================================================
static void look_both(Motion *mlist, int mcnt, int *tokens)
{
    Motion2 m2list[1000];

    parse1_minimal(mlist,m2list);

    if (!OPT_silent) {
        printf("\n\n");
    }

    parse1(mlist,mcnt);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST parse1\n\n");
        bothPrint(mlist, m2list);
    }

    //breakpt();

    while(fillCircleGaps(mlist,mcnt));
    fillCircleGaps_minimal(m2list);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST fillCircleGaps\n\n");
        bothPrint(mlist, m2list);
    }

    //breakpt();

    findWeakCircles(mlist,mcnt);
    findWeakCircles_minimal(m2list);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST findWeakCircles\n\n");
        bothPrint(mlist, m2list);
    }

    //breakpt();

    findBestPulses(mlist,mcnt);
    findBestPulses_minimal(m2list);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST findBestPulse\n\n");
        bothPrint(mlist, m2list);
    }

    //breakpt();

    markLast(mlist,mcnt);
    markLast_minimal(m2list);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST markLast\n\n");
        bothPrint(mlist, m2list);
    }

    //breakpt();

    tokenize(mlist,mcnt);
    tokenize_minimal(m2list,tokens);

    if (!OPT_silent) {
        lookPrint(mlist,mcnt);
        printf("\nPOST tokenize\n\n");
        bothPrint(mlist, m2list);
    }

    //breakpt();
}
#endif

//===========================================================================
// getline2()
//===========================================================================
static int getline2(FILE *fp, char *line, char *end)
{
    int c = getc(fp);
    if (c==EOF) return 0;

    end--;
    while(c!='\n' && c!=EOF && line != end) {
        *(line++) = c;
        c = getc(fp);
    }
    *line = 0;
    return 1;
}

//===========================================================================
// getline()
//===========================================================================
static int getline(FILE *fp, Motion *m)
{
    float list[6];
    int idx = 0;
    char line[1000];
    char *s, *e;

    while(1) {
        if (!getline2(fp,line,line+sizeof(line))) {
            return 0;
        }
        s = line;

        while(1) {
            while(isspace(*s)) s++;

            if (*s == '#' || *s == 0) {
                break;
            }

            list[idx] = strtod(s,&e);
            if (!e || e==s) {
                printf("ERROR parsing line:\n    %s\n",line);
                fprintf(stderr,"Error parsing line:\n    %s\n",line);
                break;
            }
            s = e;
            idx++;
            if (idx >= sizeof(list)/sizeof(list[0])) break;
        }

        if (idx) {
            while (idx < sizeof(list)/sizeof(list[0])) {
                list[idx++] = 0.0;
            }
            m->b_dtime  = 0;
            m->b_atime  = list[0];
            m->b_angle  = list[1];
            m->b_imp    = list[2];
            m->b_minmag = list[3];
            m->b_maxmag = list[4];
            m->b_flags  = list[5];
            m->b_deltamag = m->b_maxmag - m->b_minmag;
            if (m->b_flags & MFLG_ZERO) {
                m->b_deltamag = m->b_maxmag;
            }


            if (m->b_angle == 0xff) {
                m->b_imp    = 0;
                m->b_minmag = 0;
                m->b_maxmag = 0;
                m->b_flags  = 0;
                m->b_deltamag = 0;
            }
            return 1;
        }
    }
}

//===========================================================================
// get() - read the file
//===========================================================================
static int get(FILE *in, Motion *m, Motion *end)
{
#if 0
    int atime = 0;
#endif
    int cnt = 0;

    //
    // start with a pause
    //
    m->b_atime  = 0;
    m->b_angle  = 0xff;
    m++;
    cnt++;

    while(m != end-1 && getline(in,m)) {
        (m-1)->b_dtime = m->b_atime - (m-1)->b_atime;
#if 0
        m->b_atime = atime;
        atime += m->b_dtime;
#endif
        m++;
        cnt++;
    }

    //
    // end with a pause
    //
    m->b_atime  = 0;
    m->b_dtime  = 0;
    m->b_angle  = 0xff;
    m++;
    cnt++;

    m->b_angle = 0xfe;
    return cnt;
}


//===========================================================================
// compareToken()
//===========================================================================
static void compareToken(
            int *ignoreMotion, 
            int *ignoreSpell, 
            int *val, 
            int stok, 
            int mtok)
{
    int mshape = TOKEN_SHAPE(mtok);
    int mdir   = TOKEN_DIR(mtok);
    int mangle = TOKEN_ANGLE(mtok);
    int sshape = TOKEN_SHAPE(stok);
    int sdir   = TOKEN_DIR(stok);
    int sangle = TOKEN_ANGLE(stok);
    int d;
    int add = 0;

    *ignoreMotion = ignoreMotions[mshape];
    *ignoreSpell  = ignoreSpells[sshape];
    *val        = 10000;
    
    switch(sshape) {
        case SSHP_PAUSE:
            ASSERT(!"Pause in spell - not allowed");
            *val = scorePause[mshape];
            break;

        case SSHP_CIRCLE:
        case SSHP_OCIRCLE:
            *val = scoreCircleMiss;
            switch(mshape) {
                case MSHP_CLEAN_CIRCLE_PLUS:
                case MSHP_DIRTY_CIRCLE_PLUS:
                case MSHP_CLEAN_CIRCLE:
                case MSHP_DIRTY_CIRCLE:
                case MSHP_CIRCLE_GAP:
                    if (mdir != sdir) break;
#if FIX_CIRCLE_OCT_COMPARE
//printf("TODO:CONFIRM FIX_CIRCLE_OCT_COMPARE IS A GOOD IDEA!!!!\n");
                    d = adiff(mangle,sangle);
                    d = ABS(d);
                    if (d>2) break;
#else
                    d = a2oct(mangle) - a2oct(sangle);
                    if (d) break;
#endif
                    *val = scoreCircleMatch;
                    break;

                case MSHP_PULSE:
                case MSHP_PULSE_PLUS:
#if FIX_CIRCLE_OCT_COMPARE
//printf("TODO:CONFIRM FIX_CIRCLE_OCT_COMPARE IS A GOOD IDEA!!!!\n");
                    d = adiff(mangle,sangle);
                    d = ABS(d);
                    if (d>2) break;
#else
                    d = a2oct(mangle) - a2oct(sangle);
                    if (d) break;
#endif
                    *val = scoreCircleMatch;
                    break;

                case MSHP_PAUSE:
                    break;
            }
            break;
        case SSHP_PULSE:
        case SSHP_OPULSE:
            *val = scorePulseMiss;
            switch(mshape) {
                case MSHP_CLEAN_CIRCLE:
                    //add += 4;
                case MSHP_DIRTY_CIRCLE:
                    //add += 2;
                case MSHP_PULSE:
                    //add += 2;
                case MSHP_CLEAN_CIRCLE_PLUS:
                case MSHP_DIRTY_CIRCLE_PLUS:
                case MSHP_PULSE_PLUS:
                    d = adiff(mangle, sangle);
                    d = ABS(d);
                    if (d < 10) {
                        *val = scorePulse[d/2] + add;
                    }
                    break;

                case MSHP_PAUSE:
                case MSHP_CIRCLE_GAP:
                    break;
            }
            break;
        case SSHP_WILD:
            *val = 1;
            break;
        default:
            fatalError("Bad token in compareToken\n");
    }

    gCompareCnt++;

    //
    // check results of pic w_moa_score_tok: function
    //
    if (OPT_check_comparetok && OPT_do_picsim) {
        int pic_mignore;
        int pic_signore;
        int pic_match;

        picRegSet(0,picRegId(v_moa_stok),stok);
        picRegSet(0,picRegId(v_moa_mtok),mtok);
        
        w_moa_score_tok();
        
        pic_mignore = picRV(picRegId(v_moa_mignore));
        pic_signore = picRV(picRegId(v_moa_signore));
        pic_match   = picRV(picRegId(v_moa_match));

        if (*ignoreMotion > 254) {
            ASSERT(pic_mignore == 255);
        } else {
            ASSERT(*ignoreMotion == pic_mignore);
        }
        if (*ignoreSpell > 254) {
            ASSERT(pic_signore == 255);
        } else {
            ASSERT(*ignoreSpell == pic_signore);
        }
#if 0
        if (*val > 254) {
            ASSERT(pic_match == 255);
        } else {
            ASSERT(*val == pic_match);
        }
#else
        if (*val >= 0x7f) {
            ASSERT(pic_match >= 0x7f);
        } else {
            ASSERT(*val == pic_match);
        }
#endif
    }
}

//===========================================================================
// printColor()
//===========================================================================
static void printColor(int diff)
{
    if (diff<0) diff = -diff;
    printf("%s",colorStr(
        (diff)>=10 ? 1 :
        (diff)>=4  ? 3 :
        (diff)>=1  ? 4 : 2));
}

//===========================================================================
// writeResult()
//===========================================================================
static void writeResult(FILE *out, int t, int s, int m, int delta)
{
    fprintf(out,"%d.0 %d.0 %d.0 %d.0\n",t,s,m,delta);
}

//===========================================================================
// scoreSpell_pic()
//===========================================================================
static void scoreSpell_pic(PSpell *s)
{
    int score;

    gCurrentSpell = s;
    gCurrentMwanemeIndex = 0;

    w_mo7_load_spell();

    w_mo9_score_spell();

    score = picRV(picRegId(v_mo9_score));

    ASSERT(score == s->score || (score==255 && s->score>=255) );
}

//===========================================================================
// scoreSpell()
//===========================================================================
static int scoreSpell(PSpell *spell, Motion *mlist, char *path, int doprint)
{
    Motion *m;
    unsigned char *spellTokens;
    unsigned char *s;
    static int mtx[1000];
    int scnt,mcnt,slen;
    int ignoreMotion, ignoreSpell, match, val, corner, lastval = 0;
    FILE *out = 0;
    int prev_atime = 0;

    //
    // get rid of leading pause
    //
    for (s=spell->tokens; *s && TOKEN_SHAPE(*s) == SSHP_PAUSE; s++);
    spellTokens = s;

    if (doprint==2) {
        printf("               ");
        for (scnt=1, s=spellTokens; *s; scnt++, s++) {
            printf("%5s ",spellTokenStr(*s));
        }
        printf("\n               ");
        for (scnt=1, s=spellTokens; *s; scnt++, s++) {
            printf(" 0x%02x ",*s);
        }
        printf("\n          %-4d ",0);
    } else if (doprint==3) {
        printf("  %5s\n",spell->name);
        printf("  %5s     %5s\n","spell","motion");
        if (OPT_result_filename && OPT_result_filename[0]) {
            out = fopen(OPT_result_filename, "w");
        }
    }

    mtx[0] = 0;
    for (scnt=1, s=spellTokens; *s; scnt++, s++) {
        compareToken(
                &ignoreMotion, 
                &ignoreSpell, 
                &match, 
                *s, 
                TOKEN_CREATE_MO(MSHP_PULSE, 0, 0));
        mtx[scnt] = val = mtx[scnt-1] + ignoreSpell;
        if (doprint==2) {
            if (path[scnt]) {
                printColor(val-lastval);
                lastval = val;
            }
            printf("<%-4d",mtx[scnt]);
            printf("%s ",colorStr(-1));
        } else if (doprint==3 && path[scnt]) {
            printf("  %5s %d   %5s   ",
                spellTokenStr(*s),
                a2oct(a2oct(TOKEN_ANGLE(*s))),
                "skip");
            printColor(ignoreSpell);
            printf("%4d  %4d%s\n",ignoreSpell,val,colorStr(-1));
        } else if (doprint == -1) {
            path[scnt] = '<';
        }
    }
    slen = scnt;

    for (mcnt=1, m = mlist; m; mcnt++, m=m->c_next) {
        corner = mtx[0];
        compareToken(
                &ignoreMotion, 
                &ignoreSpell, 
                &match,
                TOKEN_CREATE_SP(SSHP_PULSE, 0, 0),
                m->token);
        mtx[0] = val = corner + ignoreMotion;
        if (doprint==2) {
            printf("\n%5s %02x ",motionTokenStr(m->token),0/*m->token*/);
            if (path[slen*mcnt]) {
                printColor(match-lastval);
                lastval = val;
            }
            printf("^%-4d",mtx[0]);
            printf("%s ",colorStr(-1));
        } else if (doprint==3 && path[slen*mcnt]) {
                    printf("  %5s   %d %5s   ",
                        "skip",
                        a2oct(a2oct(TOKEN_ANGLE(m->token))),
                        motionTokenStr(m->token));
            printColor(ignoreMotion);
            printf("%4d  %4d%s\n",ignoreMotion,val,colorStr(-1));
        } else if (doprint == -1) {
            path[slen*mcnt] = '^';
        }
        for (scnt=1, s=spellTokens; *s; scnt++, s++) {
            char *src = "\\";
            compareToken(&ignoreMotion, &ignoreSpell, &match, *s, m->token);

            val = match + corner;
            if (val > ignoreMotion + mtx[scnt]) {
                val = ignoreMotion + mtx[scnt];
                src="^";
            }
            if (val > ignoreSpell + mtx[scnt-1]) {
                val = ignoreSpell + mtx[scnt-1];
                src="<";
            }
            if (doprint==2) {
                if (path[slen*mcnt+scnt]) {
                    printColor(val-lastval);
                    lastval = val;
                }
                printf("%s%-4d",
                    src,val);
                printf("%s ",colorStr(-1));
            } else if (doprint==3 && path[slen*mcnt+scnt]) {
                int delta = 1000;
                switch(src[0]) {
                    case '^':
                        printf("  %5s   %d %5s   ",
                            "skip",
                            a2oct(a2oct(TOKEN_ANGLE(m->token))),
                            motionTokenStr(m->token));
                        delta = ignoreMotion;
                        if (out && !OPT_result_spell_only) {
                            writeResult(out, m->b_atime, -1, m->token,
                                            OPT_result_motion_only?0:delta);
                            prev_atime = m->b_atime;
                        }
                        break;
                    case '<':
                        printf("  %5s %d   %5s   ",
                            spellTokenStr(*s),
                            a2oct(a2oct(TOKEN_ANGLE(*s))),
                            "skip");
                        delta = ignoreSpell;
                        if (out && !OPT_result_motion_only) {
                            writeResult(out, prev_atime, *s, -1,
                                            OPT_result_spell_only?0:delta);
                        }
                        break;
                    case '\\':
                        printf("  %5s %d %d %5s   ",
                            spellTokenStr(*s),
                            a2oct(a2oct(TOKEN_ANGLE(*s))),
                            a2oct(a2oct(TOKEN_ANGLE(m->token))),
                            motionTokenStr(m->token));
                        delta = match;
                        if (out) {
                            if (OPT_result_motion_only) {
                                writeResult(out, m->b_atime, -1, m->token, 0);
                            } else if (OPT_result_spell_only) {
                                writeResult(out, m->b_atime, *s, -1, 0);
                            } else {
                                writeResult(out, m->b_atime, *s, m->token,
                                            delta);
                            }
                            prev_atime = m->b_atime;
                        }
                        break;
                    default:
                        printf("  %5s     %5s   ",
                            "????",
                            "????");
                        break;
                }
                printColor(delta);
                printf("%4d  %4d%s\n",delta,val,colorStr(-1));
            } else if (doprint==-1) {
                path[slen*mcnt+scnt] = src[0];
            }

            corner = mtx[scnt];
            mtx[scnt] = val;
        }
    }
    if (doprint==2) {
        printf("\n");
    } else if (doprint == -1) {
        int i;
        int mlen = mcnt;

        //
        // find path
        //
        path[0] = '*';

        scnt--;
        mcnt--;
        while(scnt>=0 && mcnt>=0) {
            int c = path[slen*mcnt+scnt];
            path[slen*mcnt+scnt] = '*';
            switch(c) {
                case '<':
                    scnt--;
                    break;
                case '^':
                    mcnt--;
                    break;
                case '\\':
                case '*':
                    mcnt--;
                    scnt--;
                    break;
            }
        }
        for (i=0; i<mlen*slen; i++) {
            if (path[i] != '*') path[i] = 0;
        }
    }

    if (out) {
        printf("Wrote %s\n",OPT_result_filename);
        fclose(out);
    }

    if (doprint>0 && doprint<3) {
        printf("Score vs %-25s = %8d\n",spell->name,val);
    }
    spell->score = val;
    return val;
}

//===========================================================================
// spellInit()
//===========================================================================
static void spellInit(SpellCreateInfo *si)
{
    memset(si,0,sizeof(*si));
    si->lastAngle = -1;
}

//===========================================================================
// spellCircleSub()
//===========================================================================
static unsigned char *spellCircleSub(unsigned char *t, int dir, int *oct, int optional)
{
    int dbg = DBG_LOADSPELL;
    static int table_cw[8]  = { 6,7,0,1,2,3,4,5 };
    static int table_ccw[8] = { 2,3,4,5,6,7,0,1 };
    int *table = dir<0 ? table_cw : table_ccw;

    if (optional) {
        *(t++) = TOKEN_CREATE_SP(SSHP_OCIRCLE, dir, oct2a(table[*oct]));
        if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
    } else {
        *(t++) = TOKEN_CREATE_SP(SSHP_CIRCLE,  dir, oct2a(table[*oct]));
        if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
    }
    *oct = (*oct + dir) & 7;
    return t;
}

//===========================================================================
// spellCircle()
//===========================================================================
static unsigned char *spellCircle(unsigned char *t, SpellCreateInfo *si, int cnt)
{
    int dbg = DBG_LOADSPELL;
    int oct = a2oct(si->lastAngle);
    int dir = 1;
    int prevdir;
    int cnt0 = 0, cnt1 = 0, cnt2 = 0;
    if (cnt < 0) {
        dir = -1;
        cnt = -cnt;
    }

    if (dbg) {
        printf("spellCircle dir=%s1  cnt=%d\n",
            (dir<0)?"-":"+",
            cnt);
    }

    //
    // write the pic version of the mwaneme
    //
    picSpellWrite(PIC_MWN_CREATE_CIRC(dir,cnt));

    //
    // Circles always end with 
    //   - lastAngle pointing the way it is supposed to and 
    //   - 2nd to last optional circle pulse perpendicular to end direction
    //
    //   For example, if end angle is oct==2 (right)
    //      tokens = ... CW4 CW3 CW2 oCW1 oCW0 oCW7
    //

    si->lastAngle = oct2a(oct);

    switch(TOKEN_SHAPE(t[-1])) {
        case SSHP_PAUSE:
            *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE, 0, oct2a(oct));
            if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
            oct = (oct - dir) & 7;
            cnt0 = 1;
            cnt1 = 2+8-1-2 + (8*(cnt-1));
            cnt2 = 3;
            // NOTE: same lastAngle
            break;
            
        case SSHP_CIRCLE:
        case SSHP_OCIRCLE:
            prevdir = TOKEN_DIR(t[-1]);
            if (prevdir == dir) {
                t -= 3;
                oct = (oct - prevdir) & 7;
                cnt0 = 0;
                cnt1 = 2 + 8 - 2 + (8*(cnt-1));
                cnt2 = 3;
                // NOTE: same lastAngle
            } else {
                oct = (oct + 4) & 7;
                *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE, 0, oct2a(oct));
                if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
                *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE, 0, oct2a((oct+prevdir)&7));
                if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
                *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE, 0, oct2a(oct));
                if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
                oct = (oct - dir) & 7;
                cnt0 = 2;
                cnt1 = 2+8-2-2 + (8*(cnt-1));
                cnt2 = 3;   // circlSlop=5
                si->lastAngle = oct2a(oct);
            }
            break;

        case SSHP_OPULSE:
        case SSHP_PULSE:
            oct = (oct - dir) & 7;
            cnt0 = 3;
            cnt1 = 2+8-3-2 + (8*(cnt-1));
            cnt2 = 3;
            // NOTE: same lastAngle
            break;

        default:
            fatalError("Bad token in spellCircle() \n");
    }

    while(cnt0--) {
        t = spellCircleSub(t, dir, &oct, 1);
    }
    while(cnt1--) {
        t = spellCircleSub(t, dir, &oct, 0);
    }
    while(cnt2--) {
        t = spellCircleSub(t, dir, &oct, 1);
    }
    si->prevCircleCnt = cnt;
    return t;
}

//===========================================================================
// spellPulse()
//===========================================================================
static unsigned char *spellPulse(unsigned char *t, SpellCreateInfo *si, int oct)
{
    int dbg = DBG_LOADSPELL;
    int revoct = (oct + 4) & 7;
    int optional = 0;
    int dir, octc, octprev = a2oct(si->lastAngle);
    int cnt;

    if (dbg) {
        printf("spellPulse oct=%d\n",
            oct);
    }

    //
    // write the pic version of the mwaneme
    //
    picSpellWrite(PIC_MWN_CREATE_PULSE(oct));

    switch(TOKEN_SHAPE(t[-1])) {
        case SSHP_PAUSE:
            *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE,0,oct2a(oct));
            if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
            break;

        case SSHP_OPULSE:
        case SSHP_PULSE:
            //
            // same direction as last pulse?
            //
            if (revoct == octprev) {
                optional = 1;
                *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE,0,oct2a(oct));
                if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }

            //
            // opposite direction of previous
            //
            } else if (oct == octprev) {
                break;

            //
            // some other angle (less)
            //
            } else if (odiff(oct,octprev) > 0) {
                while(octprev != oct) {
                    t = spellCircleSub(t, 1, &octprev, 1);
                }
            } else {
                while(octprev != oct) {
                    t = spellCircleSub(t, -1, &octprev, 1);
                }
            }
            break;
            
        case SSHP_CIRCLE:
        case SSHP_OCIRCLE:
            dir = TOKEN_DIR(t[-1]);
            octc = a2oct(si->lastAngle);
//printf("octc=%d\n",octc);
            // t now points to the CW2  in example above
            t -= 4;

//printf("t[]=%02x\n",*t);
            // oct now points up (4) in example above
            octc = (octc - dir * 2) & 7;
            cnt = -1;

//printf("octc=%d cnt=%d\n",octc,cnt);

            if (si->prevCircleCnt > 1) {
                t -= 1;
                octc = (octc - dir) & 7;
                cnt -= 1;
            }
//printf("t[]=%02x\n",*t);
//printf("octc=%d cnt=%d\n",octc,cnt);

//printf("S octc(%d) +dir(%d) !=? oct=%d\n",octc,dir,oct);
            while(((octc+dir)&7) != oct) {
//printf("+ octc(%d) +dir(%d) !=? oct=%d\n",octc,dir,oct);
                t = spellCircleSub(t, dir, &octc, 0);
                cnt++;
            }
//printf("E octc(%d) +dir(%d) !=? oct=%d\n",octc,dir,oct);

            //
            // if less than full circle add an extra optional circle
            //
            if (cnt < 0) {
//printf("EXTRA\n");
                for (cnt=0; cnt<8; cnt++) {
                    t = spellCircleSub(t, dir, &octc, 1);
                }
            }

//printf("2 octc=%d\n",octc);
            t = spellCircleSub(t, dir, &octc, 1);
//printf("1 octc=%d\n",octc);
            t = spellCircleSub(t, dir, &octc, 1);
//printf("0 octc=%d revoct=%d\n",octc,revoct);
            break;

        default:
            fatalError("Bad token in spellCircle() \n");
    }

    if (optional) {
        *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE,0,oct2a(revoct));
        if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
    } else {
        *(t++) = TOKEN_CREATE_SP(SSHP_PULSE,0,oct2a(revoct));
        if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
    }
#if 0
    *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE,0,oct2a(oct));
    if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
#endif
    si->lastAngle = oct2a(revoct);
    si->prevCircleCnt = 0;
    return t;
}

//===========================================================================
// spellPause()
//===========================================================================
static unsigned char *spellPause(unsigned char *t, SpellCreateInfo *si)
{
    int dbg = DBG_LOADSPELL;
    ASSERT(!"Inserting pause into spell (no longer supported)");
    *(t++) = TOKEN_CREATE_SP( SSHP_PAUSE, 0, 0);
    if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }
    si->prevCircleCnt = 0;
    return t;
}

//===========================================================================
// createSpell()
//===========================================================================
static void createSpell(PSpell *s)
{
    int dbg = DBG_LOADSPELL;
    SpellCreateInfo si[1];
    unsigned char *t = s->tokens;
    const char *p;
    int oct;
    int startAngle = -1;

    spellInit(si);

    startAngle = -1;
    for (p=s->mwanemes; *p; p++) {
        switch(*p) {
            case 'e':
            case 'i':
            case 'a':
            case 'o':
            case 'u':
            case 'y':
            case ' ':
                break;
            case 'd': startAngle = oct2a(4); break;
            case 'k': startAngle = oct2a(2); break;
            case 'g': startAngle = oct2a(0); break;
            case 'f': startAngle = oct2a(6); break;
            case 'h': startAngle = oct2a(3); break;
            case 's': startAngle = oct2a(1); break;
            case 'm': startAngle = oct2a(7); break;
            case 'l': startAngle = oct2a(5); break;
            default:
                fatalError("ERROR: BAD PHONEME CHARACTER: %c\n    in '%s'\n",
                    *p,s->mwanemes);
        }
        if (startAngle >= 0) {
            si->lastAngle = startAngle;
            break;
        }
    }

    ASSERT(startAngle >= 0 && *p);

    //
    // write initial oct
    //
    picSpellWrite(a2oct(startAngle));

    *(t++) = TOKEN_CREATE_SP( SSHP_PAUSE,0,0);
    if (dbg) { printf("    %s  %02x\n",spellTokenStr(*(t-1)),*(t-1)); }

    for (p=s->mwanemes; *p; p++) {
        if (dbg) { printf("mwaneme %c\n",*p); }
        switch(*p) {
            case 'e': t = spellCircle(t,si,+2); break;
            case 'i': t = spellCircle(t,si,+3); break;
            case 'a': t = spellCircle(t,si,-1); break;
            case 'o': t = spellCircle(t,si,-2); break;
            case 'u': t = spellCircle(t,si,-3); break;
            case 'y': t = spellCircle(t,si,+1); break;
            case 'd': t = spellPulse(t,si,4); break;
            case 'k': t = spellPulse(t,si,2); break;
            case 'g': t = spellPulse(t,si,0); break;
            case 'f': t = spellPulse(t,si,6); break;
            case 'h': t = spellPulse(t,si,3); break;
            case 's': t = spellPulse(t,si,1); break;
            case 'm': t = spellPulse(t,si,7); break;
            case 'l': t = spellPulse(t,si,5); break;
            case ' ': t = spellPause(t,si); break;
            default:
                fatalError("ERROR: BAD PHONEME CHARACTER: %c\n    in '%s'\n",
                    *p,s->mwanemes);
        }
        if (t-s->tokens > sizeof(s->tokens)-10) {
            printf("ERROR\n\n\n");
            fprintf(stderr,"\n\n\n");
            fprintf(stderr,"ERROR: SPELL TOO LONG:\n");
            fprintf(stderr,"     in '%s'\n",
                s->mwanemes);
            fprintf(stderr,"\n\n\n");
            exit(1);
        }
    }

    //
    // write end of pic mwanemes
    //
    picSpellWrite(0);

    oct = a2oct(si->lastAngle);
    oct = (oct+4)&7;
    *(t++) = TOKEN_CREATE_SP(SSHP_OPULSE,0,oct2a(oct));

    //*(t++) = TOKEN_CREATE_SP(SSHP_WILD, 0, 0);
    //*(t++) = TOKEN_CREATE_SP(SSHP_WILD, 0, 0);
    *t = STOKEN_END;

    s->len = t-s->tokens;
    //return t - s->tokens;
}

#if 0
//===========================================================================
// spellLen() - find # of tokens in spell
//===========================================================================
static int spellLen(PSpell *s)
{
    unsigned char *t = s->tokens;
    while(*t) t++;
    return t-s->tokens;
}
#endif

//===========================================================================
// buildSpell() - 
//===========================================================================
static int buildSpell(PSpell *s)
{
    ASSERT(s->mwanemes[0]);
    createSpell(s);
    ASSERT(s->tokens[0] != STOKEN_END);
    //return spellLen(s);

    return s->len;
}

//===========================================================================
// buildSpell_pic() - 
//===========================================================================
static void buildSpell_pic(PSpell *s)
{
    int i;

    gCurrentSpell = s;
    gCurrentMwanemeIndex = 0;

    w_mo7_load_spell();

    for (i=0;;i++) {
        int val = picRV(RAMBUF_STOKENS+i);
        s->pic_tokens[i] = val;
        if (!val) break;
        ASSERT(val < MAX_TOKENS);
    }

    gCurrentSpell = 0;
    gCurrentMwanemeIndex = 0;
}

#define SORT_BY_LEN 1
#if SORT_BY_LEN
//===========================================================================
// spellCmpName()
//===========================================================================
static int spellCmpName(const void *a, const void *b)
{
    const PSpell *pa = (const PSpell*)a;
    const PSpell *pb = (const PSpell*)b;
    return strcmp(pa->name,pb->name);
}

//===========================================================================
// spellCmpSortId()
//===========================================================================
static int spellCmpSortId(const void *a, const void *b)
{
    const PSpell *pa = (const PSpell*)a;
    const PSpell *pb = (const PSpell*)b;
    int cmp = pa->sortId - pb->sortId;
    if (cmp) return cmp;
    return strcmp(pa->name,pb->name);
}
#endif


//===========================================================================
// buildAllSpells() - 
//===========================================================================
static void buildAllSpells(PSpell *slist)
{
    int spellinfo=2;
    int spellid = 1;
    PSpell *s;
    PSpell *badspell = 0;
    int maxlen = 0;
    int max_maxscore = 0;
    int max_holdoff  = 0;

    gCurrentSpell = 0;
    gCurrentMwanemeIndex = 0;


#if SORT_BY_LEN
    int spellcnt;
    //
    // count spells 
    // create spells
    //
    for (s=slist; s->name; s++) {

        buildSpell(s);
        maxlen = s->len > maxlen ? s->len : maxlen;
        max_maxscore = s->maxscore > max_maxscore ? s->maxscore : max_maxscore;
        max_holdoff = s->holdoff > max_holdoff ? s->holdoff : max_holdoff;
        if (OPT_onespell) break;
    }
    spellcnt = OPT_onespell ? 1 : s-slist;

    //
    // sort spells
    //
    if (spellcnt > 1 ) {
        int i;
        //
        // sort spells by name
        //
        qsort(slist, spellcnt, sizeof(PSpell), spellCmpName);

        //
        // assign id & sortId
        //
        for (i=0; i<spellcnt; i++) {
            if (i && !strcmp(slist[i].baseName, slist[i-1].baseName)) {
                slist[i].id = slist[i-1].id;
                slist[i].sortId = slist[i-1].sortId;
                slist[i].flags &= ~SPELL_FLG_FIRST;
            } else {
                slist[i].id = spellid++;
                slist[i].flags |=  SPELL_FLG_FIRST;
                slist[i].sortId = slist[i].len;
            }
        }

        //
        // sort spells by sortId (==len), name
        //
        qsort(slist, spellcnt, sizeof(PSpell), spellCmpSortId);
    }
#endif

    gMaxMaxscore = max_maxscore;
    gMaxHoldoff = max_holdoff;

    //
    // build spells again and write info
    //
    for (s=slist; s->name; s++) {

        gCurrentSpell = s;
        gCurrentMwanemeIndex = 0;
        picSpellWrite(spellinfo&0xff);
        picSpellWrite(spellinfo>>8);

        picSpellWrite(s->maxscore);
        picSpellWrite(s->holdoff);
        ASSERT(s->id>0);
        ASSERT(s->id<0xff);
        picSpellWrite(s->id);

        s->spellinfo = spellinfo;

        buildSpell(s);
        if (OPT_onespell || !OPT_silent || OPT_showspell) {
            printf("Spell %25s  LEN=%d\n",s->name,s->len);
        }
        if (s->len > maxlen) {
            maxlen = s->len;
        }

        spellinfo += 2;
        if (OPT_onespell) break;
    }
    if (!OPT_silent || OPT_showspell) {
        printf("MAX SPELL LEN=%d    total spells=%d\n",
            maxlen,
            (spellinfo-2)/2);
    }
    ASSERT(maxlen < MAX_TOKEN_CNT);

    if (!OPT_do_picsim) {
        return;
    }

    //
    // try pic version
    //
    for (s=slist; s->name; s++) {
        int i,j,e;

        
        if (OPT_showspell) {
            printf("======= spell %s   %s\n",s->name, s->mwanemes);
        }
        buildSpell(s);
        buildSpell_pic(s);

        if (OPT_showspell) {
            printf("MWN: ");
            for (i=0; s->pic_mwanemes[i] || i<2; i++) {
                printf("%02x ",s->pic_mwanemes[i]);
            }
            printf("%02x\n",0);
            printf("            Ctok   PICtok\n");
        }
        i = 1;
        j = 0;
        e = 1;
        while(e) {
            int tok_c   = s->tokens[i];
            int tok_pic = s->pic_tokens[j];
            e=0;
            if (OPT_showspell) {
                printf("[%3d] ",(i>j)?i:j);
            }
            if (tok_c) {
                if (OPT_showspell) {
                    printf(" %s %02x   ",
                        spellTokenStr(tok_c),
                        tok_c);
                }
                i++;
                e=1;
            } else {
                if (OPT_showspell) {
                    printf(" %s %02x   ",
                        spellTokenStr(0),
                        0);
                }
            }
            if (OPT_showspell) {
                printf("%s",tok_c == tok_pic?" ":"*");
            }
            if (tok_c != tok_pic) {
                badspell = badspell ? badspell : s;
            }
            if (tok_pic) {
                if (OPT_showspell) {
                    printf(" %02x %s\n",
                        tok_pic,
                        spellTokenStr(tok_pic));
                }
                j++;
                e=1;
            } else {
                if (OPT_showspell) {
                    printf(" %02x %s\n",
                        0,
                        spellTokenStr(0));
                }
            }
        }
        if (badspell) {
            fprintf(stderr,"ERROR IN w_check_motion_spell!!\n");
            fprintf(stderr,"Run: fmtb_parse -s -q -w\n");
            fprintf(stderr,"Run: fmtb_parse -s -q -w %s\n",
                s->name);
        }
        if (OPT_onespell) break;
    }
    if (badspell) {
        fprintf(stderr,"ERROR IN w_check_motion_spell!!\n");
        fprintf(stderr,"Run: fmtb_parse -s -q -w\n");
        fprintf(stderr,"Run: fmtb_parse -s -q -w %s\n",
            badspell->name);
    } else if (OPT_showspell && !OPT_onespell) {
        printf("\nAll spells loaded OK!\n\n");
    }
    ASSERT(!badspell);

    gCurrentSpell = 0;
    gCurrentMwanemeIndex = 0;

    if (OPT_mwaneme_filename && !OPT_onespell) {
        int snum = 0;
        char *filename2;
        int last_byte = -1;
        int fnlen = strlen(OPT_mwaneme_filename);
        FILE *fp = fopen(OPT_mwaneme_filename,"w");
        FILE *fp2;
        if (!fp) {
            fprintf(stderr,"Unable to write file '%s'\n",OPT_mwaneme_filename);
            ASSERT(!"Could not write file");
        }
        filename2 = malloc(fnlen+2);
        memcpy(filename2,OPT_mwaneme_filename,fnlen+1);
        filename2[fnlen+1]=0;
        if (filename2[fnlen-4] == '.') {
            filename2[fnlen]   = filename2[fnlen-1];
            filename2[fnlen-1] = filename2[fnlen-2];
            filename2[fnlen-2] = filename2[fnlen-3];
            filename2[fnlen-3] = '.';
            filename2[fnlen-4] = '2';
        } else {
            filename2[fnlen] = '2';
        }
        fp2 = fopen(filename2,"w");


        gCurrentSpell = slist;
        gCurrentMwanemeIndex = 0;

        fprintf(fp,";\n");
        fprintf(fp,"; AUTOGENERATED FILE - DO NOT EDIT\n");
        fprintf(fp,";\n");
        fprintf(fp,"; Created with: fmtb_parse -W%s\n",OPT_mwaneme_filename);
        fprintf(fp,";\n");
        fprintf(fp,"\n");

        ASSERT(max_maxscore <= 0xff);
        fprintf(fp,"MAX_MAXSCORE    equ 0x%02x\n",max_maxscore);
        fprintf(fp,"MAX_HOLDOFF     equ 0x%02x\n",max_holdoff);
        fprintf(fp,"\n");


        fprintf(fp,"w_motion_spells:\n");

        fprintf(fp2,";\n");
        fprintf(fp2,"; AUTOGENERATED FILE - DO NOT EDIT\n");
        fprintf(fp2,";\n");
        fprintf(fp2,"; Created with: fmtb_parse -W%s\n",OPT_mwaneme_filename);
        fprintf(fp2,";\n");
        fprintf(fp2,"\n");
        fprintf(fp2,"w_motion_spell_strings:\n");

        while(1) {
            int maxs, hold, id;
            int sil, sih, oct0,cnt=0;
            s = gCurrentSpell;
            sil = picSpellRead();
            sih = picSpellRead();
            maxs = picSpellRead();
            hold = picSpellRead();
            id   = picSpellRead();
            oct0 = picSpellRead();

            if (s && s->name) {
                ASSERT(oct0 >=0 && oct0 <= 7);
                ASSERT(s->baseName && s->baseName[0]);
                if (s->flags & SPELL_FLG_FIRST) {
                    fprintf(fp2,"\n");
                    if (s->flags & SPELL_FLG_INFO) {
                        fprintf(fp2,"#if DBG_SPELL_MSG_IS_SPELL_NAME\n");
                    }
                    fprintf(fp2,"w_spellinfo_%s:\t; (%04x)\n",
                        s->baseName,
                        snum);
                    fprintf(fp2,"\tdb\t\"you cast %s\\0\"\n",
                        s->baseName);
                    if (s->flags & SPELL_FLG_INFO) {
                        fprintf(fp2,"#endif\n");
                    }
                }

                fprintf(fp,"\n");
                fprintf(fp,"; (%04x) spell %s (%s) len=%d %s\n",
                    snum,
                    s->name,
                    s->mwanemes,
                    s->len,
                    mwanemeDescStr(s->mwanemes));
                snum++;
                fprintf(fp,"; w_spellinfo_%s\n",s->baseName);
                fprintf(fp,"\tdb\t");
                cnt = 2;
                if (last_byte >= 0) {
                    fprintf(fp,"0x%02x, ",last_byte);
                    last_byte = -1;
                    cnt++;
                }
                fprintf(fp,"LOW  w_spellinfo_%s, ",s->baseName);
                fprintf(fp,"HIGH w_spellinfo_%s, ",s->baseName);
                fprintf(fp,"0x%02x, ",maxs);
                fprintf(fp,"0x%02x, ",hold);
                fprintf(fp,"0x%02x, ",id);
                fprintf(fp,"0x%02x",oct0);
                cnt+=6;
                
            } else {
                ASSERT(gCurrentSpell==0);
                ASSERT(sih==(PIC_MWN_END_SPELLS>>8));
                ASSERT(sil==(PIC_MWN_END_SPELLS&0xff));
                ASSERT(oct0==-1);

                fprintf(fp,"; End of spells\n");
                fprintf(fp,"    db  ");
                if (last_byte >= 0) {
                    fprintf(fp,"0x%02x, ",last_byte);
                    last_byte = -1;
                }
                fprintf(fp,"0x%02x, 0x%02x\n",
                    PIC_MWN_END_SPELLS&0xff,
                    PIC_MWN_END_SPELLS>>8);
                fprintf(fp,"\n");
                break;
            }

            while(1) {
                int val;
                if (last_byte == -1) {
                    last_byte = picSpellRead();
                }
                if (PIC_MWN_END(last_byte)) {
                    fprintf(fp,"\n");
                    break;
                }
                if (cnt & 1) {
                    fprintf(fp,", 0x%02x",last_byte);
                    cnt++;
                    last_byte = -1;
                    continue;
                }
                
                val = picSpellRead();

                if (cnt >= 8) {
                    fprintf(fp,"\n\tdb\t0x%02x, 0x%02x",last_byte,val);
                    cnt = 2;
                } else {
                    fprintf(fp,", 0x%02x, 0x%02x",last_byte,val);
                    cnt+=2;
                }
                last_byte = -1;

                if (PIC_MWN_END(val)) {
                    fprintf(fp,"\n");
                    break;
                }
            }
        }


        fclose(fp);
        fprintf(fp2,"\n");
        fclose(fp2);
        free(filename2);
    }

    gCurrentSpell = 0;
    gCurrentMwanemeIndex = 0;
}


//===========================================================================
// checkCompareTok()
//===========================================================================
static void checkCompareTok(void)
{
    OPT_check_comparetok = 1;
    int ignoreMotion;
    int ignoreSpell;
    int val;
    int ss,ms,sa,ma,stok,mtok;
    int cyc0 = picGetCycle();
    int cyc1;
    int cnt = 0;
    int cyca,cycb;


    if (!OPT_silent) {
        printf("begin checkCompareTok\n");
    }
    for (ma=0; ma<32; ma++) {
    for (sa=0; sa<32; sa+=2) {
    for (ss=0; ss<SSHP_CNT; ss++) {
        if (ss == SSHP_PAUSE) continue;
        if (ss == SSHP_WILD) continue;
    for (ms=0; ms<MSHP_CNT; ms++) {
        if (ms == MSHP_PAUSE) continue;


        stok = (ss<<4) | ((sa << 4) & 0x80) | ((sa << 1) & 0xe);
        mtok = (ms<<4) | ((ma << 3) & 0x80) | ((ma     ) & 0xf);
        cyca = picGetCycle();
        compareToken(
            &ignoreMotion, 
            &ignoreSpell, 
            &val, 
            stok, 
            mtok);
        cycb = picGetCycle();
        if (OPT_showtime) {
            printf("cmptok:  %d cyc\n",cycb-cyca);
        }
        cnt++;
    }}}}

    if (!OPT_silent) {
        printf("end   checkCompareTok\n");
    }

    cyc1 = picGetCycle();
    if (!OPT_silent || OPT_showtime) {
        printf("Compare: %d token compares in %d cycles (%5.3f sec)\n",
            cnt,
            cyc1-cyc0,
            (cyc1-cyc0)/1000000.0);
        printf("%d cycles (%8.6f sec) per compare avg\n",
             (cyc1-cyc0)/cnt,
            ((cyc1-cyc0)/cnt)/1000000.0);
    }

    picSetCycle(cyc0);

    OPT_check_comparetok = 0;
}

//===========================================================================
// checkSpells()
//===========================================================================
static int checkSpells(PSpell *slist, Motion *mlist, int mcnt)
{
    Motion *m, *mp;
    char *path;
    PSpell *s, *best = 0, *best2=0;
    int scnt = 0;
    int startAngle = 0;

    if (OPT_do_picsim) {
        w_mo8_load_data_tables();

        if (OPT_check_comparetok) {
            checkCompareTok();
            OPT_check_comparetok = 0;
        }
    }

    //
    // ignore leading pause(s)
    //
    while(mlist && TOKEN_SHAPE(mlist->token) == MSHP_PAUSE) {
        mlist = mlist->c_next;
    }
    if (!mlist) return -1;

    //
    // ignore trailing pause(s)
    //
    for (mp=m=mlist; m; m=m->c_next) {
        if (TOKEN_SHAPE(m->token) != MSHP_PAUSE) {
            mp = m;
        }
    }
    mp->c_next = 0;

    if (!mlist) return -1;

#if MINIMAL
    startAngle = -1;    // not used
#else
    //
    // find start angle
    //
#if 0
    for (m=mlist; m; m=m->c_next) {
        int shape = TOKEN_SHAPE(m->token);
        if (shape == MSHP_PULSE ||
            shape == MSHP_PULSE_PLUS) {
            startAngle = TOKEN_ANGLE(m->token);
            break;
        }
    }
#else
    startAngle = TOKEN_ANGLE(mlist->token);
#endif
#endif
    (void)startAngle;

    //
    // count len of max spell
    //
    for (s=slist; s->name; s++) {
        int len;
        len = buildSpell(s);
        if (len > scnt) {
            scnt = len;
        }
        if (OPT_onespell) break;
    }

    scnt++;
    path = malloc((mcnt+1) * scnt);

    for (s=slist; s->name; s++) {
        memset(path,0,mcnt*scnt);
        scoreSpell(s,mlist,path,-1);
        if (!OPT_silent ||
             (OPT_onespell && !OPT_silent2) ||
            OPT_result_filename) {
            //
            // 2nd time with printing
            //
            scoreSpell(s,mlist,path,2);
            scoreSpell(s,mlist,path,3);
        }
        if (!best || best->score > s->score) {
            if (best && best->id != s->id) {
                best2 = best;
            }
            best = s;
        } else if (best->id != s->id && (!best2 || best2->score > s->score)) {
            best2 = s;
        }
#if 0
        if (OPT_do_picsim) {
            scoreSpell_pic(s);
        }
#endif
        if (OPT_onespell) break;
    }

#if 0
    if (!OPT_onespell && OPT_do_picsim) {
        //
        // check that load of last spell +1 returns PIC_MWN_END_SPELLS
        //
        w_mo7_load_spell();
        ASSERT(picRV(picRegId(v_mo_spellinfol)) == (PIC_MWN_END_SPELLS&0xff));
        ASSERT(picRV(picRegId(v_mo_spellinfoh)) == (PIC_MWN_END_SPELLS>>8));
    }
#endif

    free(path);

#if 0
    //
    // find 2nd best
    //
    best2 = 0;
    for (s=slist; s->name; s++) {
        if (s==best) continue;
        if (s->id==best->id) continue;
        if (!best2) best2 = s;
        if (best2->score > s->score) {
            best2 = s;
        }
        if (OPT_onespell) break;
    }
#endif

    gBestSpell1 = best;
    gBestSpell2 = best2;
    gChooseSpell = 0;

    if (best->score <= best->maxscore && 
        (!best2 || best2->score - best->score > best->holdoff)) {
        gChooseSpell = best;
    }

    if (!OPT_filelist && OPT_silent && !OPT_silent2) {
        printf("\nRESULT:Scores\n");
        for (s=slist; s->name; s++) {
            printf("RESULT: %s  %05d  %s  (%s)  x(%s %s)%s\n",
                s==best  ? "*":
                s==best2 ? "+":" ",
                s->score,
                s->name,
                s->mwanemes ? s->mwanemes : "",
                gCurrentSpell ? gCurrentSpell->name : "",
                gCurrentSpell && gCurrentSpell->mwanemes ?
                        gCurrentSpell->mwanemes : "",
                (s==gCurrentSpell) ? "SAME" : "");

            if (OPT_onespell) break;
        }
    }
    gBestSpell1_m = gBestSpell1;
    gBestSpell2_m = gBestSpell2;
    gChooseSpell_m = gChooseSpell;
    return 0;
}


//===========================================================================
// checkSpells_pic()
//===========================================================================
static int checkSpells_pic(PSpell *slist, PSpell *best1, PSpell *best2)
{
    int best1_info, best2_info;
    int best1_score, best2_score;
    int choose_info;
    int highest_score = gMaxMaxscore + gMaxHoldoff;

    gCurrentSpell = slist;
    gCurrentMwanemeIndex = 0;

    if (!OPT_use_picsim) {
        w_mo8_check_spells();
    }

    best1_score =  picRV(picRegId(v_mo_best1_score));
    best2_score =  picRV(picRegId(v_mo_best2_score));
    best1_info  = (picRV(picRegId(v_mo_best1_infoh)) << 8) |
                   picRV(picRegId(v_mo_best1_infol));
    best2_info  = (picRV(picRegId(v_mo_best2_infoh)) << 8) |
                   picRV(picRegId(v_mo_best2_infol));
    choose_info = (picRV(picRegId(v_mo_spellinfoh)) << 8) |
                   picRV(picRegId(v_mo_spellinfol));

    ASSERT(!choose_info || choose_info == best1_info);


    if (best1 && best2) {
        ASSERT(best1_score == best1->score || 
                (best1_score>=highest_score && best1->score>=highest_score));
        if (!OPT_onespell) {
            ASSERT(best2_score == best2->score ||
                (best2_score>=highest_score && best2->score>=highest_score));
        }
        //ASSERT(best1_info  == best1->spellinfo);
        //ASSERT(best2_info  == best2->spellinfo);
    }

    if (!best1 ||
        !best2 ||
        best1_info != best1->spellinfo ||
        best2_info != best2->spellinfo) {
        PSpell *s;
        best1 = best2 = 0;
        for (s=slist; s->name; s++) {
            if (best1_info == s->spellinfo) {
                best1 = s;
                if (best2) break;
            }
            if (best2_info == s->spellinfo) {
                best2 = s;
                if (best1) break;
            }
            if (OPT_onespell) break;
        }
        ASSERT(best1 || best1_info==0);
        ASSERT(best2 || best2_info==0);
        if (!OPT_use_original && !OPT_use_minimal) {
            gBestSpell1 = best1;
            gBestSpell2 = best2;
            if (best1) {
                best1->spellinfo = best1_info;
                best1->score = best1_score;
            }
            if (best2) {
                best2->spellinfo = best2_info;
                best2->score = best2_score;
            }
        }
    }

    if (!OPT_onespell && best1 && best2) {
#if 1
        if (choose_info == best1_info) {
            if (!(best1->score <= best1->maxscore)) {
                printf("FAILED best1->score <= best1->maxscore\n%d  %d\n",
                    best1->score,best1->maxscore);
            }
#if 0
            if (!(best2->score > best2->maxscore)) {
                printf("FAILED best2->score > best2->maxscore\n%d  %d\n",
                    best2->score,best2->maxscore);
            }
#endif
            if (!(best2->score - best1->score > best1->holdoff)) {
                printf("FAILED best2->score - best1->score > best1->holdoff\n %d  %d %d \n",
                    best2->score, best1->score, best1->holdoff);
            }
            gChooseSpell = best1;
        } else {
            int proba = (best1->score <= best1->maxscore);
#if 0
            int probb = (best2->score > best2->maxscore);
#else
            int probb = 1;
#endif
            int probc = (best2->score - best1->score > best1->holdoff);
            ASSERT(!proba || !probb || !probc);
            gChooseSpell = 0;
        }
#else
        if (best1->score <= best1->maxscore &&
            best2->score + best1->holdoff > best1->score) {
            if (gChooseSpell) {
                ASSERT(gChooseSpell->score == best1->score);
            }
            gChooseSpell = best1;
        } else {
            gChooseSpell = 0;
        }
#endif
    }
    gCurrentSpell = 0;
    gCurrentMwanemeIndex = 0;
    return 0;
}

//===========================================================================
// checkSpellName()
//===========================================================================
static void checkSpellName(const char *name)
{
    const char *s = name;
    //
    // Spell name must be all lowercase letters, optionally followed by 1
    // or more digits (necessary for tryem script)
    //
    while(isalpha(*s) || *s=='_') s++;
    if (s == name) {
        fprintf(stderr,"Bad spell name: '%s'\n",name);
        ASSERT(!"must begin with letters");
    }
    while(isdigit(*s)) s++;
    if (*s) {
        fprintf(stderr,"Bad spell name: '%s'\n",name);
        ASSERT(!"any digits must come at end of spell name");
    }
}


//===========================================================================
// spellCmp()
//===========================================================================
static int spellCmp(const void *a, const void *b)
{
    const PSpell *pa = *((const PSpell**)a);
    const PSpell *pb = *((const PSpell**)b);
    return strcmp(pa->name,pb->name);
}

//===========================================================================
// initSpells()
//===========================================================================
static PSpell *initSpells(Spell *s,PSpell *old)
{
#if SORT_BY_LEN
    PSpell **sortList;
    int id=1;
#endif
    PSpell *slist;
    int scnt,dcnt;
    int i;

    if (old) free(old);
    for (scnt=0; s[scnt].name; scnt++);

    slist = calloc((scnt+1),  sizeof(PSpell));
    sortList = calloc((scnt+1),  sizeof(PSpell*));

    scnt = dcnt = 0;
    for (; s[scnt].name; scnt++) {
        char *np;
        checkSpellName(s[scnt].name);
        mwanemeDescStr(s[scnt].mwanemes);
        if (!OPT_allspells && (s[scnt].flags & SPELL_FLG_WAND)==0) continue;
        if (OPT_vital && (s[scnt].flags & SPELL_FLG_VITAL)==0) continue;
        slist[dcnt].flags    = s[scnt].flags;
        slist[dcnt].maxscore = s[scnt].maxscore;
        slist[dcnt].holdoff  = s[scnt].holdoff;
        slist[dcnt].name     = s[scnt].name;
        slist[dcnt].mwanemes = s[scnt].mwanemes;
        slist[dcnt].baseName = strdup(s[scnt].name);
        for (np = slist[dcnt].baseName; *np; np++) {
            if (!islower(*np)) break;
        }
        *np = 0;
        sortList[dcnt] = &slist[dcnt];
        ASSERT(s[scnt].maxscore + s[scnt].holdoff < 254);
        dcnt++;
    }   
    
#if SORT_BY_LEN
    qsort(sortList, dcnt, sizeof(PSpell*), spellCmp);

    for (i=0; i<dcnt; i++) {
        if (i && !strcmp(sortList[i]->baseName, sortList[i-1]->baseName)) {
            sortList[i]->id = sortList[i-1]->id;
            sortList[i]->flags &= ~SPELL_FLG_FIRST;
        } else {
            sortList[i]->id = id++;
            sortList[i]->flags |=  SPELL_FLG_FIRST;
        }
        sortList[i]->sortId = sortList[i]->id;
    }
    
    free(sortList);
#endif

    return slist;
}

//===========================================================================
// crosscheck()
//===========================================================================
int crosscheck(PSpell *sp, PSpell *slist)
{
    Motion mlist[CNT_MAX];
    Motion *m;
    int tm = 1;
    unsigned char *t;

    //
    //  bit    mode
    //   0     include optional circles
    //   1     make pulses plus
    //   2     include optional pulses
    int mode = 0;

    printf("========== Crosscheck spell %s = %s\n",sp->name,sp->mwanemes);
    gCurrentSpell = sp;

    buildSpell(sp);
    t = sp->tokens;

    //
    // copy tokens to mlist
    //
    memset(mlist,0,sizeof(mlist));
    m  = mlist;
    tm = 1;
    while(1) {
        int shape = TOKEN_SHAPE(*t);
        int dir   = TOKEN_DIR(*t);
        int angle = TOKEN_ANGLE(*t);

        if (*t == STOKEN_END) break;

        switch(shape) {
            case SSHP_CIRCLE:
                shape = MSHP_CLEAN_CIRCLE;
                break;

            case SSHP_OCIRCLE:
                shape = (mode & 0x1) ? MSHP_DIRTY_CIRCLE : -1;
                break;

            case SSHP_PULSE:
                shape = (mode & 0x2) ? MSHP_PULSE_PLUS : MSHP_PULSE;
                break;

            case SSHP_OPULSE:
                shape = (mode & 0x4) ? 1 : MSHP_PULSE;
                break;

            case SSHP_WILD:
                shape = -1;
                break;

            case SSHP_PAUSE:
                shape = -1;
                break;
        }

        if (shape != -1) {
            m->token = TOKEN_CREATE_MO(shape,dir,angle);
            m->b_atime = tm;
            m->c_next = m+1;
            m->c_prev = m-1;
            m++;
        }

        t++;
        tm++;
    }

    m->token = MTOKEN_END;
    m->c_next = 0;
    mlist->c_prev = 0;

    checkSpells(slist, mlist, 1 + m - mlist);

    gCurrentSpell = 0;
    return 0;
}


//===========================================================================
// x2d()
//===========================================================================
int x2d(int c)
{
    c = tolower(c);
    if (isdigit(c)) return c-'0';
    if (islower(c)) return c-'a'+10;
    return 0;
}

//===========================================================================
// getRaw()
//===========================================================================
int getRaw(Motion *mlist, FILE *in)
{
    int v[8];
    int idx=0;
    int half=0;
    int loop=1;
    Motion *m = mlist;
    int st = 0;
    while(loop) {
        int c = getc(in);
        if (c == EOF) break;
        switch(st) {
            case 0:
                    if (c=='\'') st++;
                    break;
            case 1:
                    if (c=='m') st=102;
                    else if (c=='a') st=202;
                    else st=0;
                    break;

            case 100:
                    if (c=='\'') st++;
                    break;
            case 101:
                    if (c=='m') st++;
                    else st=100;
                    break;
            case 102:
                    if (isxdigit(c)) {
                        v[0] = x2d(c)<<4;
                        st++;
                    } else st=100;
                    break;
            case 103:
                    if (isxdigit(c)) {
                        v[0] += x2d(c);
                        st++;
                    } else st=100;
                    break;
            case 104:
                    if (c=='\'') {
                        m->c_prev = m-1;
                        m->c_next = m+1;
                        m->token = v[0];
                        m++;
                    }
                    st=100;
                    break;

            case 199:
                    half = 0;
                    st++;
            case 200:
                    if (c=='\'') st++;
                    break;
            case 201:
                    idx = 0;
                    if (c=='a') st++;
                    else st=199;
                    break;
            case 202:
                    if (isxdigit(c)) {
                        v[idx] = x2d(c)<<4;
                        st++;
                    } else st=199;
                    break;
            case 203:
                    if (isxdigit(c)) {
                        v[idx] += x2d(c);
                        idx++;
                        if (idx < 8) st=202;
                        else st=204;
                    } else st=199;
                    break;
            case 204:
                    if (c=='\'') {
                        OPT_raw = 0;
                        idx = 0;
#if 0
printf("X: %02x %02x %02x %02x %02x %02x %02x %02x\n",
v[idx+0],
v[idx+1],
v[idx+2],
v[idx+3],
v[idx+4],
v[idx+5],
v[idx+6],
v[idx+7]);
#endif
                        while(idx < 8) {
                            if (half) {
#if 0
printf("B: %02x %02x %02x %02x\n",
v[idx+0],
v[idx+1],
v[idx+2],
v[idx+3]);
#endif
                                m->b_imp = (v[idx+0]<<8) + v[idx+1];
                                m->b_minmag = v[idx+2];
                                m->b_flags  = v[idx+3];
                                if (m->b_flags & MFLG_ZERO) {
                                    m->b_deltamag = m->b_maxmag;
                                } else {
                                    m->b_deltamag = m->b_maxmag -
                                                    m->b_minmag;
                                }
                                m++;
                                idx += 4;
                                half = 0;
                                continue;
                            }
                            memset(m,0,sizeof(*m));
#if 0
printf("A: %02x %02x %02x %02x\n",
v[idx+0],
v[idx+1],
v[idx+2],
v[idx+3]);
#endif
                            m->b_dtime = (v[idx+0]<<8) + v[idx+1];
                            m->b_angle = v[idx+3];
                            if (m == mlist) {
                                m->b_atime = 0;
                            } else {
                                m->b_atime = (m-1)->b_atime + 
                                             (m-1)->b_dtime;
                            }
                            if (m->b_angle < 0x20) {
                                m->b_maxmag = v[idx+2];
                                half = 1;
                            } else {
                                m++;
                                if (m->b_angle == 0xfe) {
                                    m--;
                                    memset(m,0,sizeof(*m));
                                    loop = 0;
                                    break;
                                }
                            }
                            idx += 4;
                        }
                        st=200;
                    } else {
                        st=199;
                    }
                    break;


            default:
                    st=0;
                    break;
        }
    }

    if (m==mlist) {
        fprintf(stderr,"ERROR: file has no data!\n");
        exit(1);
    }

    if (OPT_export) {
        Motion *m2;
        if (OPT_raw) {
            fprintf(stderr,"ERROR: file is not in aXXXXXX format!\n");
            exit(1);
        }
        for (m2=mlist; m2!=m; m2++) {
            printf("%8.1f %8.1f %8.1f %8.1f %8.1f %8.1f\n",
                (double)m2->b_atime,
                (double)m2->b_angle,
                (double)m2->b_imp,
                (double)m2->b_minmag,
                (double)m2->b_maxmag,
                (double)m2->b_flags);
        }
        m2--;
        if (m2->b_angle < 0x20) {
            printf("%8.1f %8.1f %8.1f %8.1f %8.1f %8.1f\n",
                (double)(m2->b_atime + m2->b_dtime),
                255.0,
                0.0,
                0.0,
                0.0,
                0.0);
        }
        exit(0);
    }

    if (OPT_raw) {
        m->c_prev = m-1;
        m->c_next = m+1;
        m->token = MTOKEN_END;
        m++;

        mlist->c_prev = 0;
        (m-1)->c_next = 0;
        OPT_use_minimal = 1;
        OPT_do_picsim = 0;
        printf("Found %d tokens\n",m-mlist);
    } else {
        printf("Found %d acc records\n",m-mlist);
    }
    
    return m-mlist;
}

//===========================================================================
// runit()
//===========================================================================
int runit(PSpell *slist, FILE *in)
{
    Motion mlist[CNT_MAX];
    int tokens_min[CNT_MAX];
    int tokens_pic[CNT_MAX];
    int atime_min[CNT_MAX];
    int atime_pic[CNT_MAX];
    int *tokens_use = 0;
    int *atime_use = 0;
    Motion *m;
    int *tc, *tp;
    int mcnt=0;

    memset(mlist,0,sizeof(mlist));

    if (OPT_raw) {
        mcnt = getRaw(mlist,in);
    } else {
        mcnt = get(in,mlist,mlist+(sizeof(mlist)/sizeof(mlist[0])));
    }
    
    if (mcnt) {

        memset(atime_min,0,sizeof(atime_min));
        memset(atime_pic,0,sizeof(atime_pic));
        tokens_min[0] = MTOKEN_END;
        tokens_pic[0] = MTOKEN_END;
        initialPrint(mlist);

        //
        // Use one of these functions here to generate tokens
        //
        //  look(mlist,mcnt);
        //  look_minimal(mlist, tokens);
        //  look_pic(mlist, tokens);
        //  look_both(mlist, mcnt, tokens);
        //  look_all3(mlist, mcnt, tokens_min, tokens_pic);
        //
        if (OPT_raw) {
            tokens_use = 0;
            atime_use = 0;
        } else if (OPT_use_original) {
            tokens_use = 0;
            atime_use = 0;
        } else if (OPT_use_minimal) {
            tokens_use = tokens_min;
            atime_use  = atime_min;
        } else if (OPT_use_picsim) {
            tokens_use = tokens_pic;
            atime_use  = atime_pic;
        } else {
            tokens_use = tokens_pic;
            atime_use  = atime_pic;
        }
        if (OPT_raw) {
            // no conersion needed
        } else if (OPT_use_original && !OPT_use_all) {
            look(mlist,mcnt);
            OPT_do_picsim = 0;
        } else if (OPT_use_minimal && !OPT_use_all) {
            look_minimal(mlist, tokens_min, atime_min);
            OPT_do_picsim = 0;
        } else if (OPT_use_picsim && !OPT_use_all) {
            look_pic(mlist, tokens_pic, atime_pic, slist);
            OPT_do_picsim = 1;
        } else {
            look_all3(mlist, mcnt, tokens_min, tokens_pic, atime_min, atime_pic);
        }

        if (!OPT_silent || OPT_show_tokens) {
            int *at_p = atime_pic;
            int *at_m = atime_min;
            //
            // print tokens
            //
            m = mlist;
            tc = tokens_min;
            tp = tokens_pic;
            printf("  %8s     %11s     %8s\n",
                "Original",
                "Minimal ",
                "Picsim  ");
            printf("  %8s     %11s     %8s\n",
                "--------",
                "-----------",
                "--------");
            while(1) {
                int mask = 0xff;
                if (!m && *tc==MTOKEN_END && *tp==MTOKEN_END) {
                    break;
                }
                if (!m) {
                    printf("  %5s %2s  @ ","","");
                } else if (m && TOKEN_SHAPE(m->token) != MSHP_CIRCLE_GAP &&
                    *tc!=MTOKEN_END && TOKEN_SHAPE(*tc) == MSHP_CIRCLE_GAP) {
                    printf("  %5s %2s  @ ","","");
                } else {
                    printf("  %5s %02x ",
                        m?motionTokenStr(m->token):"END  ",
                        m?m->token:0);
                    // pause?
                    if (m && m->token == TOKEN_CREATE_MO(MSHP_PAUSE,0,0)) {
                        printf("\n");
                        m = m?m->c_next:0;
                        continue;
                    }
                    if (m && TOKEN_SHAPE(m->token) == MSHP_CIRCLE_GAP &&
                        *tc!=MTOKEN_END && TOKEN_SHAPE(*tc) != MSHP_CIRCLE_GAP) {
                        printf(" @\n");
                        m = m?m->c_next:0;
                        continue;
                    }
                    if (TOKEN_SHAPE(*tc) == MSHP_PULSE_PLUS ||
                        TOKEN_SHAPE(*tc) == MSHP_PULSE) {
                        mask = 0x7f;
                    }

                    printf(" %s ",
                        (*tc!=MTOKEN_END && m &&
                            ((*tc)&mask)==((m->token)&mask)) ?
                                                " " : "@");
                    m = m->c_next;
                }

                printf(" %02x %5s %02x",
                    (*tc!=MTOKEN_END)?*tc:0,
                    (*tc!=MTOKEN_END)?motionTokenStr(*tc):"END  ",
                    (*tc!=MTOKEN_END)?*tc:0);

                printf("  %s ",*tc==*tp ? " " : "*");

                if (atime_use == atime_pic && *at_m && !*at_p) {
                    atime_use = atime_min;
                }
                if(*tc!=MTOKEN_END) {
                    tc++;
                    at_m++;
                }

                printf(" %02x %5s     token[%2d]\n",
                    (*tp!=MTOKEN_END)?*tp:0,
                    (*tp!=MTOKEN_END)?motionTokenStr(*tp):"END  ",
                    tp-tokens_pic);

                if(*tp!=MTOKEN_END) {
                    tp++;
                    at_p++;
                }
            }
        }
        if (!OPT_silent || OPT_show_tokens || OPT_showtime) {
            int cyc = picGetCycle();
            if (cyc) {
                printf("tokenize: %7d cycles;  %5.3f sec\n",
                    cyc,
                    (float)cyc/1000000);
            }
        }


        if (tokens_use) {
            int *t; 
            int *tm; 

            //
            // copy tokens to mlist
            //
            memset(mlist,0,sizeof(mlist[0])*mcnt);
            m  = mlist;
            t  = tokens_use;
            tm = atime_use;
            while(1) {
                m->token = *t;
                m->b_atime = *tm;
                if (*t == MTOKEN_END) break;
                if (m != mlist) (m-1)->c_next = m;
                m++;
                t++;
                tm++;
            }
        }

        if (!OPT_quick) {
            int err = 0;
            int cyc0 = picGetCycle();
            if (OPT_use_all || !OPT_use_picsim) {
                err = checkSpells(slist, mlist,mcnt);
            }
            picSetCycle(cyc0);
            if (OPT_do_picsim && !err) {
                err = checkSpells_pic(slist, gBestSpell1, gBestSpell2);
            }
            if (!err) {
                printf("RESULT2:%-15s %-55s %4d %-15s %4d %-15s  (%6.3f sec)\n",
                    gChooseSpell?gChooseSpell->name:"NONE",
                    OPT_input_filename?OPT_input_filename:"stdin",
                    gBestSpell1?gBestSpell1->score:0,
                    gBestSpell1?gBestSpell1->name:"NONE",
                    gBestSpell2?gBestSpell2->score:0,
                    gBestSpell2?gBestSpell2->name:"NONE",
                    (float)picGetCycle()/1000000.0);
                if (OPT_use_all && 
                    (gChooseSpell != gChooseSpell_m ||
                     gBestSpell1 != gBestSpell1_m ||
                     gBestSpell2 != gBestSpell2_m)) {
                    printf(
                        "MISMTCH:%-15s %-55s %4d %-15s %4d %-15s\n",
                        gChooseSpell_m?gChooseSpell_m->name:"NONE",
                        OPT_input_filename?OPT_input_filename:"stdin",
                        gBestSpell1_m?gBestSpell1_m->score:0,
                        gBestSpell1_m?gBestSpell1_m->name:"NONE",
                        gBestSpell2_m?gBestSpell2_m->score:0,
                        gBestSpell2_m?gBestSpell2_m->name:"NONE");
                    printf("^cmdline: %s\n",gCmdline);
                }
            }
        }
    }
    return 0;
}

//===========================================================================
// runlist()
//===========================================================================
int runlist(PSpell *slist, FILE *in)
{
    PSpell *spell;
    int rv = 0;
    while(1) {
        char line[300];
        char spellnamebuf[100];
        char *s;
        char *filename = 0;
        char *spellname = 0;
        int   spellnamelen = 0;
        FILE *fp;

        if (!getline2(in,line,line+sizeof(line))) {
            printf("Z overall score= %d",rv);
            return rv;
        }
        filename = line;
        for (s=line; *s && *s != ':'; s++);
        if (s[0] && s[1]) {
            spellname = s+1;
            *s = 0;
        }
        if (s-line <= 4) continue;
        s -= 4;
        if (strcmp(s,".txt")) continue;
        while(s!=line && *s!='/') s--;
        if (*s == '/') s++;
        if (!strncmp(s,"wpic",4)) {
            char *st = s + 4;
            for (; *st=='_'; st++);
            for (s=st; islower(*s); s++);
            if ((1 || isdigit(*s) || *s=='_' || *s=='.') && s != st) {
                memcpy(spellnamebuf,st,s-st);
                spellnamebuf[s-st] = 0;
                spellname = spellnamebuf;
            }
        }

        spell = 0;
        if (spellname) {
            spellnamelen = strlen(spellname);
            for (spell=slist; spell->name; spell++) {
                if (!strncmp(spell->name,spellname,spellnamelen)) break;
                if (OPT_onespell) {
                    spell=0;
                    break;
                }
            }
            if (!spellnamelen || !spell || !spell->name) {
                spell = 0;
            }
        }

        fp = fopen(filename,"r");
        if (!fp) {
            fprintf(stderr,"Could not open file '%s'\n",filename);
        } else {
            int thresh1;
            int thresh2;
            int s1, s2;
            char *result1 = "?";
            char *result2 = "???";
            int color = -1;
            int score = 0;
            int score2 = 0;
            
            OPT_input_filename = filename;
            gBestSpell1 = 0;
            gBestSpell2 = 0;
            runit(slist,fp);

            thresh1 = 10;
            thresh2 = 10;
            s1 = gBestSpell1->score;
            s2 = gBestSpell2->score;
            if (spell) {
                if (!strncmp(spellname, gBestSpell1->name,spellnamelen)) {
                    score2  = s1-s2;
                    if ((gBestSpell1->flags & SPELL_FLG_DUMMY) &&
                        (gBestSpell2->flags & SPELL_FLG_DUMMY)) {
                        result1 = "B";
                        result2 = "dummy";
                        color   = 2;
                        score   = 0;
                    } else if (s2 < thresh2*2) {
                        result1 = "P";
                        result2 = "TOO CLOSE";
                        color   = 3;
                        score   = 1000;
                    } else if (s1 < thresh1) {
                        result1 = "A";
                        result2 = "good";
                        color   = 2;
                        score   = -1000;
                    } else if ((s2-thresh2) - (s1-thresh1) <(thresh1+thresh2)){
                        result1 = "E";
                        result2 = "TOO CLOSE";
                        color   = 3;
                        score   = 1000;
                    } else if (s1 < thresh1*2) {
                        result1 = "C";
                        result2 = "close";
                        color   = 4;
                        score   = 1000;
                    } else {
                        result1 = "D";
                        result2 = "FAR";
                        color   = 6;
                        score   = 100;
                    }
                    if (score < -100) score = -100;
                    if (score >  100) score =  100;
                } else {
                    score2  = -s1;
                    if (gBestSpell1->flags & SPELL_FLG_DUMMY) {
                        result1 = "B";
                        result2 = "dummy";
                        color   = 2;
                        score   = 0;
                    } else if (s1 < thresh1*1) {
                        result1 = "T";
                        result2 = "FALSE +";
                        color   = 1;
                        score   = 10000;
                    } else if (s1 < thresh1*2) {
                        result1 = "S";
                        result2 = "TOO CLOSE";
                        color   = 1;
                        score   = 1000;
                    } else {
                        result1 = "Q";
                        result2 = "MISS";
                        color   = 5;
                        score   = 100;
                    }
                    if (score < -100) score = -100;
                }
            } else {
                score = 0;
                score2  = s1-s2;
                if (score < -100) score = -100;

                result1 = "V";
                result2 = "??";
                color   = -1;
            }
        
            printf("%s%s %05x %5d %5d %9s %4d %4d %20s %20s (%15s) %s %s\n",
                result1,
                colorStr(color),
                score + score2 + 0x40000,
                score,
                score2,
                result2,
                s1,
                s2,
                gBestSpell1->name,
                gBestSpell2->name,
                spellname?spellname:"",
                filename,
                colorStr(-1));
        
            rv += score;
            OPT_input_filename = 0;
        }
    }
}

//===========================================================================
// usage()
//===========================================================================
static void usage(void)
{
    printf(
    "Usage: fmtb_parse [OPTIONS] < wpic_file.txt\n"
    "Usage: fmtb_parse [OPTIONS] <spell> < wpic_file.txt\n"
    "   Parse motions from wpic_file.txt and compare against spells.\n"
    "   If <spell> argument is supplied only compare against that\n"
    "   one spell.\n"
    "Options:\n"
    "   -d  describe.  Show all spells with mwanemes.\n"
    "   -v  vital.  Only vital spells (default = all in wand)\n"
    "   -A  ALL.  Use all spells (default = all in wand)\n"
    "   -q  quick.  Parse but do not compare.\n"
    "   -s  silent.  Just show results.\n"
    "   -S  very silent.  Just show top 2 matches.\n"
    "   -c  no color.  Do not use color escape characters.\n"
    "   -a  no addr.  Do not print pointer addresses.\n"
    "   -o  original.  Use tokens generated by original algorithm.\n"
    "   -M  classic-minimal.   Use classic minimal algorithm.\n"
    "   -m  minimal.   Use tokens generated by minimal algorithm.\n"
    "   -x  experimental-minimal.   Use experimental minimal alg.\n"
    "   -p  picsim.    Use tokens generated by picsim algorithm.\n"
    "   -3  all3.      Use all 3 algorithms & compare (default).\n"
    "   -t  show tokens & difference btwn old & new algorithm.\n"
    "   -w  show spell tokens & quit\n"
    "   -C  no crash.  Do not crash on assert (just exit).\n"
    "   -L  cmdline.  show command line.\n"
    "   -f  file list.  Read file:spell list from stdin\n"
    "   -T  timing info.   Print cycle times\n"
    "   -R  Raw data.  Parse rawdata from wand out as motions\n"
    "   -e  export. Just spew wpic data to stdout (implies -R).\n"
    "   -?  print this usage message.\n"
    "\n"
    "   -X         cross check all spells\n"
    "   -X<spell>  cross check <spell>\n"
    "   -XX<mwan>  use <mwan> as mwaneme string\n"
    "   -r<file>   write results to <file> (for wg input)\n"
    "   -W<file>   write mwaneme file for assembly (wmspells.inc) to <file>\n"
    "   -l         supply literal mwanemes\n"
    );

    exit(1);
}

//===========================================================================
// main()
//===========================================================================
int main(int argc, char *argv[])
{
    PSpell lspell;
    PSpell lxspell;
    PSpell *slistAll;
    PSpell *slist = 0;
    PSpell *xspell = 0;
    PSpell *s;
    char **argp;
    FILE *infile = 0;
    int rv = 1;
    int OPT_cmdline=0;
    int save_allspells;
    int save_vital;

    save_vital= OPT_vital;
    save_allspells = OPT_allspells;
    OPT_allspells = 1;
    OPT_vital = 0;
    slistAll = initSpells(spells,0);
    OPT_allspells = save_allspells;
    OPT_vital = save_vital;

    for(argp=&argv[1]; *argp; argp++) {
        if (!strcmp(*argp,"-?")) {
            usage();
        } else if (!strcmp(*argp,"-e")) {
            OPT_silent=1;
            OPT_silent2=1;
            OPT_export=1;
            OPT_raw=1;
        } else if (!strcmp(*argp,"-R")) {
            OPT_raw=1;
        } else if (!strcmp(*argp,"-L")) {
            OPT_cmdline=1;
        } else if (!strcmp(*argp,"-d")) {
            OPT_describe=1;
        } else if (!strcmp(*argp,"-A")) {
            OPT_allspells=1;
        } else if (!strcmp(*argp,"-v")) {
            OPT_vital=1;
        } else if (!strcmp(*argp,"-w")) {
            OPT_showspell=1;
        } else if (!strcmp(*argp,"-f")) {
            OPT_filelist=1;
        } else if (!strcmp(*argp,"-T")) {
            OPT_showtime=1;
        } else if (!strcmp(*argp,"-C")) {
            OPT_nocrash=1;
        } else if (!strcmp(*argp,"-a")) {
            OPT_noaddr=1;
        } else if (!strcmp(*argp,"-c")) {
            OPT_nocolor=1;
        } else if (!strcmp(*argp,"-s")) {
            OPT_silent=1;
        } else if (!strcmp(*argp,"-S")) {
            OPT_silent=1;
            OPT_silent2=1;
        } else if (!strcmp(*argp,"-q")) {
            OPT_quick=1;
        } else if (!strcmp(*argp,"-o")) {
            OPT_use_original=1;
        } else if (!strcmp(*argp,"-M")) {
            OPT_use_classic=1;
        } else if (!strcmp(*argp,"-x")) {
            OPT_use_experimental=1;
        } else if (!strcmp(*argp,"-m")) {
            OPT_use_minimal=1;
        } else if (!strcmp(*argp,"-p")) {
            OPT_use_picsim=1;
        } else if (!strcmp(*argp,"-3")) {
            OPT_use_all=1;
        } else if (!strcmp(*argp,"-t")) {
            OPT_show_tokens=1;
        } else if (!strncmp(*argp,"-X",2)) {
            if (OPT_crosscheck) {
                fprintf(stderr,"only use -X once\n");
                usage();
            }
            OPT_crosscheck=1;
            if (argp[0][2] == 'X') {
                memset(&lxspell,0,sizeof(lxspell));
                lxspell.flags = SPELL_FLG_VITAL;
                lxspell.name = *argp + 3; 
                lxspell.mwanemes = *argp + 3; 

                checkSpellName(lxspell.mwanemes);

                xspell = &lspell;

            } else if (argp[0][2]) {
                for (s=slistAll; s->name; s++) {
                    if (!strcmp(*argp+2,s->name)) {
                        break;
                    }
                }
                if (s->name) {
                    xspell = s;
                } else {
                    fprintf(stderr,"No spell named '%s'\n",*argp+2);
                    usage();
                }
            }
        } else if (!strncmp(*argp,"-W",2)) {
            OPT_mwaneme_filename = *argp + 2;
        } else if (!strncmp(*argp,"-r",2)) {
            OPT_result_filename = *argp + 2;
        } else if (!strncmp(*argp,"-l",2)) {
            if (!argp[0][2]) {
                fprintf(stderr,"Please supply mwanemes with -l\n");
                usage();
            }
            if (slist) {
                fprintf(stderr,"Do not use -l with spell-name\n");
                usage();
            }
            memset(&lspell,0,sizeof(lspell));
            lspell.flags = SPELL_FLG_VITAL;
            lspell.name = *argp + 2; 
            lspell.mwanemes = *argp + 2; 

            checkSpellName(lspell.mwanemes);

            slist = &lspell;
            OPT_onespell=1;

        } else if (argp[0][0] == '-') {
            fprintf(stderr,"Option '%s' unknown\n",*argp);
            usage();
        } else {
            s = 0;
            if (!slist) {
                for (s=slistAll; s->name; s++) {
                    if (!strcmp(*argp,s->name)) {
                        slist = s;
                        OPT_onespell=1;
                        break;
                    }
                }
            }
            if (!s || !s->name) {
                if (infile) {
                    fprintf(stderr,"extra argument: '%s'\n",*argp);
                    usage();
                }
                infile = fopen(*argp, "r");
                if (infile) {
                    OPT_input_filename=*argp;
                } else {
                    fprintf(stderr,"bad argument: '%s'\n",*argp);
                    usage();
                }
            }
        }
    }
    {
        char *p = gCmdline;
        p += sprintf(p,"fmtb_parse ");
        for(argp=&argv[1]; *argp; argp++) {
            p += sprintf(p,"%s ",*argp);
        }
        acAssertSetUserFunc(fmtbAssertFailed);
    }
    if (OPT_cmdline) {
        printf("Cmdline: %s\n",gCmdline);
    }

    if (!slist) {
        slistAll = initSpells(spells,slistAll);
    }

    if (!slist) slist = slistAll;

    if (OPT_quick && OPT_silent) OPT_show_tokens=1;

    if (OPT_nocrash) {
        acAssertMode(AC_ASSERT_EXIT);
    }

    if (OPT_mwaneme_filename && OPT_onespell) {
        fprintf(stderr,"Do not use -W flag and specify a spell together\n");
        usage();
    }
    if (OPT_filelist && OPT_crosscheck) {
        fprintf(stderr,"Do not use -X and -f flags together\n");
        usage();
    }
    if (OPT_input_filename && OPT_crosscheck) {
        fprintf(stderr,"Do not use a filename with -X flag\n");
        usage();
    }
    if (OPT_input_filename && OPT_filelist) {
        fprintf(stderr,"Do not use a filename with -f flag\n");
        usage();
    }
    if (OPT_result_filename && !OPT_result_filename[0]) {
        fprintf(stderr,"-r requires filename (no space)\n");
        usage();
    }
    if (OPT_result_filename && !OPT_onespell) {
        OPT_result_motion_only = 1;
        OPT_onespell = 1;
    }

    if (OPT_use_original +
        OPT_use_classic +
        OPT_use_experimental +
        OPT_use_minimal +
        OPT_use_picsim > 1) {
        fprintf(stderr,"Only use one of -o -m -p or -3 options\n");
        usage();
    }
    if (OPT_use_classic || OPT_use_experimental) {
        OPT_use_minimal = 1;
    }
    if ((OPT_use_minimal || OPT_use_original) &&
        !OPT_use_picsim &&
        !OPT_use_all) {
        OPT_do_picsim = 0;

    }
    if (OPT_describe) {
        describeSpells(slist);
        exit(0);
    }

    init_pic();

    buildAllSpells(slist);
    if (OPT_showspell) {
        int cyc = picGetCycle();
        printf("BuildSpell done:   %7d cycles       %5.3f sec\n",
            cyc,
            (float)cyc/1000000);
        exit(0);
    }
    if (OPT_mwaneme_filename) {
        exit(0);
    }
    picSetCycle(0);

    if (OPT_crosscheck) {
        if (xspell) {
            rv = crosscheck(xspell,slist);
        } else {
            for (xspell=slistAll; xspell->name; xspell++) {
                rv |= crosscheck(xspell, slistAll);
            }
        }
    } else if (infile) {
        rv = runit(slist,infile);
    } else if (OPT_filelist) {
        rv = runlist(slist,stdin);
    } else {
        rv = runit(slist,stdin);
    }
    if (infile) {
        fclose(infile);
        infile = 0;
    }
    if (!OPT_silent || OPT_showtime) {
        printf("PIC: %7d cycles;      %5.3f sec\n",
            picGetCycle(),
            (float)picGetCycle()/1000000.0);
        printf("%d token compares total\n",gCompareCnt);
    }
    return rv;
}

//===========================================================================
// preventWarnings()
//===========================================================================
void preventWarnings(void)
{
    readTokens(0,0);
    eraseLocalVars();
    writeM2List(0,0);
    readM2List(0,0);
    w_a2oct();
    w_oct2a();
    writeM1List(0,0);
    w_mo_read();
    w_adiff();
    w_shp_write();
    w_shp_prev();
    quad2a(0);
    w_mo1_parse1();
    w_mo2_savenext();
    w_mo2_fill_circle_gaps();
    w_mo3_find_weak_circles();
    w_mo4_find_best_pulses();
    w_mo5_marklast();
    w_mo6_tokenize();
    w_check_motion_spell();
    scoreSpell_pic(0);
}


This file Copyright (C) 2006 by Nathan (Acorn) Pooley
Go to TOP Wand page
Go to Acorn's personal webpage
Go to Hogwarts website: www.gotohogwarts.com
Snout: www.snout.org/game
Gadgets of Justice Unlimited
Snout GC (Wiki)
Snout Wiki
File created by do_doc at Wed May 30 03:26:33 PDT 2007