[comp.sources.misc] Sun Multibus to VME Adapter Switch Setting Program

lee@srs.UUCP (Lee Hasiuk) (10/13/87)

I have written a program (enclosed) which will hopefully make it much
easier for those of you faced with having to set up Sun VME to Multibus
adapters.  The program is called vme2mb and takes as arguments a
description of your device and outputs a complete list of switch
settings.

Enjoy!

Lee Hasiuk
Speech Recognition Systems
Rochester, NY
{rutgers, ames, allegra}!rochester!srs!lee

#!/bin/sh
#
# shar archiver, delete everything above the #!/bin/sh line
# and run through sh (not csh)
#
echo 'shar: extracting "vme2mb.doc" (4755 characters)'
# 'vme2mb.doc' has a checksum of 54470 on BSD and 51163 on System V.
sed 's/^X//' > vme2mb.doc << 'XXX_EOF_XXX'
Xvme2mb- Determine switch settings for a Sun VME to Multibus adapter
X
XSYNOPSIS
X
Xvme2mb {-m addr,size} {-i addr,size} {-v vec} {-d 20/24} {-o offset}
X
XDESCRIPTION
X
XGiven a description of a device in Multibus address space, produce switch
Xsettings for a Sun Microsystems VME to Multibus Adapter part # 501-1054.
XThe settings are output in an order which is helpful when setting up the
Xboard.  If the board is oriented as follows (looking at the component side):
X
X			(TOP)
X---------------      ------------------     ----------------
X| VMEbus P1   |      |   VMEbus P2    |     |  VMEbus P3   |
X------------------------------------------------------------
X|                      DIP2   DIP10                        |
X|        DIP8   DIP6                                       |
X|		       DIP1   DIP9                         |
X|        DIP7   DIP5                                       |
X|                                                          |
X|                      DIP3                                |
X| DIP12  DIP11  DIP4                                       |
X------------------------------------------------------------
X|     Mutilbus P2     |   |          Multibus P1           |   
X-----------------------   ----------------------------------
X		      (BOTTOM)
X
Xthe switch settings are output starting from the left, going
Xdown each column from top to bottom until the bottom of the right hand
Xcolumn is reached.
X
XOPTIONS
X
XAll numbers supplied to the program may be in decimal or hexadecimal notation.
XHexadecimal numbers must be preceeded by 0x or 0X.
X
X-m addr,size
XDefault: no memory address response
X
XSets the memory address space response of the board.  'addr' is the base
Xaddress, and 'size' is the number of bytes which your device uses, starting
Xat the given address.  'size' will be rounded up to 256 or the closest power
Xof two, whichever is greater, due to the implementation of the adapter board.
XMay be used with -i option below for boards with both memory and I/O response.
X
X-i addr,size
XDefault: no I/O address response
X
XSets the I/O address space response of the board.  'addr' is the base
Xaddress, and 'size' is the number of bytes which your device uses, starting
Xat the given address.  'size' will be rounded up to the closest non-zero
Xpower of two, due to the implementation of the adapter board.
XMay be used with -m option above for boards with both memory and I/O response.
X
X-v vec
XDefault: interrupt vector assumed to be in PROM
X
XSets the interrupt vector which is generated when the Multibus board requests
Xan interrupt at any level.  If the vector is not specified, the board is
Xsetup so that none of the vector switches are set.  This is the proper setting
Xwhen interrupt vectors are stored in a PROM supplied by the user.
X
X-d 20/24
XDefault: -d 20
X
XSets the number of bits that your Multibus board supplies when doing DMA
Xtransfers to the VMEbus.  The only legal values for this parameter are 20
Xor 24 (or 0x14/0x18).  BEWARE: Some Sun boards that would appear to be
X24 bit DMA devices will not work when they pass all 24 bits.  The
XXylogics 472 tape controller (xtc0) comes to mind as having this problem.
X24 bit DMA isn't necessary unless you expect your board to transfer data
Xdirectly to another VMEbus device.
X
X-o off
XDefault: -o 0x000000
X
XSets the DMA offset when setting up a 20 bit (-d 20) DMA device to do
XDMA to other than the Sun DVMA space.  Only the top 4 bits of the supplied
X24 bit value are used, the other bits must be zero.  It is extremely rare
Xfor a device to require the use of this parameter.
X
XOUTPUT
X
XThe output is a list of all the dip switches on the board, with the settings
Xfor each section of each switch.  Each switch has 8 sections which can either
Xbe ON (switch up if board held in above orientation), OFF (switch down),
Xor X.  X means that it doesn't matter which way the switch is set.
X
XEXAMPLES
X
XHere are a few vme2mb command lines for various well-known Sun devices:
X
X1) Xylogics 450 disk controller
X   Address space: Multibus I/O
X   Base address: 0xee40
X   Size: 6 bytes (rounded to 8)
X   Vector: 0x48
X   DMA address provided: 24 bits
X
X   vme2mb -i 0xee40,8 -v 0x48 -d 24
X
X2) Tapemaster 1/2 inch tape controller
X   Address space: Multibus I/O
X   Base address: 0x60
X   Size: 2 bytes
X   Vector: 0x60
X   DMA address provided: 20 bits
X
X   vme2mb -i 0x60,2 -v 0x60
X
XSEE ALSO
X
XSun VME-Multibus Adapter Board User's Manual
XPart Number 800-1193-05
XRevision: A of 25 September 1986
X
XBEWARE of earlier revisions (also labelled A, but with earlier dates),
Xthey contain incorrect information about setting up the DMA dip switch!
X
XTRADEMARKS
X
XMultibus is a trademark of Intel Corporation
X
XSun, Sun Microsystems, Sun Workstation, and DVMA are registered trademarks
Xof Sun Microsystems, Incorporated.
XXX_EOF_XXX
if test 4755 -ne "`wc -c < vme2mb.doc`"
then
    echo 'shar: transmission error on "vme2mb.doc"'
fi
chk=`sum vme2mb.doc | awk '{print $1}'`
if test 54470 -ne $chk -a 51163 -ne $chk
then
    echo 'shar: checksum error on "vme2mb.doc"'
fi
echo 'shar: extracting "Makefile" (40 characters)'
# 'Makefile' has a checksum of 46411 on BSD and 3199 on System V.
sed 's/^X//' > Makefile << 'XXX_EOF_XXX'
Xvme2mb: vme2mb.c
X	cc -o vme2mb vme2mb.c
XXX_EOF_XXX
if test 40 -ne "`wc -c < Makefile`"
then
    echo 'shar: transmission error on "Makefile"'
fi
chk=`sum Makefile | awk '{print $1}'`
if test 46411 -ne $chk -a 3199 -ne $chk
then
    echo 'shar: checksum error on "Makefile"'
fi
echo 'shar: extracting "vme2mb.c" (16196 characters)'
# 'vme2mb.c' has a checksum of 51222 on BSD and 40552 on System V.
sed 's/^X//' > vme2mb.c << 'XXX_EOF_XXX'
X#ifndef lint
Xstatic char sccsId[] = "@(#)vme2mb.c	1.1 9/30/87 SRS";
X#endif
X
X/*
X * Produce switch settings for a Sun VME to Multibus adapter.
X *
X * Lee Hasiuk
X * Speech Recognition Systems
X * 1895 Mt. Hope Ave.
X * Rochester, NY 14620
X * (716) 271-0600
X *
X */
X
X#include <stdio.h>
X#include <strings.h>
X#include <ctype.h>
X
Xtypedef short boolean;
X
X#define FALSE 0
X#define TRUE 1
X#define DONTCARE -1
X
X#define NSW 8			/* # of switches in a dip switch */
X#define NDIP (12 + 1)		/* # of dip switches on board, 0 not used */
X
X/* 
X * Structure defining a dip switch; sense is TRUE if a switch is ON for a 1
X * or FALSE is switch is ON for a 0.  Bits is an array with one element for
X * each section of a dip switch.  The value stored in element i of bits is
X * the bit number (0 = LSB, 31 = MSB) which the i+1th section of the dip
X * switch is set for.  For example, if sense was FALSE and bits[0]
X * was 23, section 1 of the dip switch would be ON if bit 23 of the number
X * being used to set it was 0.  The meaning of the number being used to set 
X * the dip switch varies with the dip switch.  If bits[n] is -1, the
X * n+1th section of the dip switch is not bit specific, and may be 
X * either a don't care or be used in some other way.
X */
Xtypedef struct {
X    boolean sense;		
X    short bits[NSW];
X} dipSw;
X
X/* The board's switches and their meanings */
X#define X DONTCARE
Xstatic dipSw dips[] = {
X	    /* 1   2   3   4   5   6   7   8  - section number of switch */
X    { FALSE,   X,  X,  X,  X,  X,  X,  X,  X },		/* DIP0 - not real */
X    { FALSE,   X,  7,  6,  5,  4,  3,  2,  1 },		/* DIP1 */
X    { FALSE,   X,  7,  6,  5,  4,  3,  2,  1 },		/* DIP2 */
X    { FALSE,  15, 14, 13, 12, 11, 10,  9,  8 },		/* DIP3 */
X    { FALSE,  15, 14, 13, 12, 11, 10,  9,  8 },		/* DIP4 */
X    { FALSE,  15, 14, 13, 12, 11, 10,  9,  8 },		/* DIP5 */
X    { FALSE,  15, 14, 13, 12, 11, 10,  9,  8 },		/* DIP6 */
X    { FALSE,  23, 22, 21, 20, 19, 18, 17, 16 },		/* DIP7 */
X    { FALSE,  23, 22, 21, 20, 19, 18, 17, 16 },		/* DIP8 */
X    {  TRUE,   0,  0,  0,  0,  0,  0,  0,  0 },		/* DIP9 */
X    {  TRUE,   0,  0,  0,  0,  0,  0,  0,  0 },		/* DIP10 */
X    {  TRUE,  23, 22, 21, 20,  X,  X,  X,  X },		/* DIP11 */
X    { FALSE,   0,  1,  2,  3,  4,  5,  6,  7 },		/* DIP12 */
X};
X#undef X
X
X/* 
X * Order that switch settings will be listed.  Looking at component side
X * of board, with VME connectors farthest from you, we go top to bottom,
X * and left to right.
X *    			    (TOP)
X * ---------------      ------------------     ----------------
X * | VMEbus P1   |      |   VMEbus P2    |     |  VMEbus P3   |
X * ------------------------------------------------------------
X * |                      DIP2   DIP10                        |
X * |        DIP8   DIP6                                       |
X * |		          DIP1   DIP9                         |
X * |        DIP7   DIP5                                       |
X * |                                                          |
X * |                      DIP3                                |
X * | DIP12  DIP11  DIP4                                       |
X * ------------------------------------------------------------
X * |     Mutilbus P2     |   |          Multibus P1           |   
X * -----------------------   ----------------------------------
X *                          (BOTTOM)
X */
Xstatic int dumpOrder[] = { 12, 8, 7, 11, 6, 5, 4, 2, 1, 3, 10, 9 };
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    static long memDescr[2] = {0, 0};
X    static long ioDescr[2] = {0, 0};
X    long intVec = -1;	/* If no vector supplied, may be PROMed, switches off */
X    long dmaSize = 20;  /* Default to 20 bit addressing; a must for xt0 */
X    long dmaOff = 0;	/* DMA offset is usually 0 - DVMA space */
X    boolean set[NDIP][NSW]; /* Settings go here */
X    char opt;
X    extern char *optarg;
X    extern int opterr;
X
X    /* Though we could run with no args, they probably want a usage message */
X    if (argc < 2)
X	usage();
X
X    opterr = 0;
X    while ((opt = getopt(argc, argv, "m:i:v:d:o:")) != EOF) {
X	switch (opt) {
X	case 'm':
X
X	    /* Multibus memory space specified, 2 numbers to parse */
X	    getVals(optarg, memDescr, 2);
X	    break;
X	case 'i':
X
X	    /* Multibus i/o space specified, 2 numbers to parse */
X	    getVals(optarg, ioDescr, 2);
X	    break;
X	case 'v':
X
X	    /* Interrupt vector supplied, 1 number to parse */
X	    getVals(optarg, &intVec, 1);
X	    break;
X	case 'd':
X	
X	    /* DMA type specified, 1 number to parse */
X	    getVals(optarg, &dmaSize, 1);
X	    break;
X	case 'o':
X
X	    /* DMA offset specified, 1 number to parse */
X	    getVals(optarg, &dmaOff, 1);
X	    break;
X	default:
X
X	    /* Huh ? */
X	    usage();
X	}
X    }
X
X    /* Given their parameters, set up the switches */
X    vme2mb(dips, set, memDescr, ioDescr, intVec, dmaSize, dmaOff);
X
X    /* Now print out the mess - somebody want to add graphics? */
X    showSettings(set, NDIP - 1, dumpOrder);
X
X    exit(0);
X}
X
X/*
X * Give a usage message and leave.
X */
Xstatic
Xusage()
X{
X    (void)fprintf(stderr, "\
XUsage: vme2mb {-m addr,size} {-i addr,size} {-v vec} {-d 20/24} {-o offset}\n");
X    exit(1);
X}
X
X/*
X * Given a set of switches, and the board's parameters, set up switches
X * as specified by the parameters.  Output goes into an array which
X * is index by [switch #][section # - 1] and a FALSE indicates an off,
X * TRUE an ON, and DONTCARE can be either.
X */
Xstatic
Xvme2mb(dips, set, mem, io, vec, dsiz, doff)
XdipSw dips[];
Xboolean set[][NSW];
Xlong mem[], io[], vec, dsiz, doff;
X{
X
X    /* Set up mem space switches; dip 8 & 6 are blksize (min 256), 7 & 5 base */
X    doAddrSpace(mem, (long) 256, dips, set, 8, 6, 7, 5);
X
X    /* Set up io space; dip 4 & 2 are blocksize (min 2), 3 & 1 base */
X    doAddrSpace(io, (long) 2, dips, set, 4, 2, 3, 1);
X
X    /* Set up interrupt vector in dip 12 */
X    doIntVec(vec, dips, set, 12);
X
X    /* Set up dma in dip 11 */
X    doDma(dsiz, doff, dips, set, 11);
X
X    /* Dip 9 and 10 are unused, usually set to all off */
X    setSw((long) 0, &dips[9], set[9]);
X    setSw((long) 0, &dips[10], set[10]);
X}
X
X/*
X * Given 20 or 24 bit DMA size and possibly a 24 bit offset, set up
X * the DMA dip switch.
X */
Xstatic
XdoDma(dsiz, doff, dip, set, sw)
Xlong dsiz, doff;	/* DMA size (20 or 24) and offset */
XdipSw dip[];		/* DIP switch descriptions */
Xshort set[][NSW];	/* Where the settings go */
Xint sw;			/* Switch to set */
X{
X
X    /* 20 bit DMA ? */
X    if (dsiz == 20) {
X
X	/* Offset can only have bits set in MSN of 24 bit address */
X	if ((doff & 0x0fffff) != 0)
X	    err("Only bits 23-20 of DMA offset may be used", (char *) 0);
X
X	/* Set offset switches */
X	setSw(doff, &dip[sw], set[sw]);
X
X	/* Force sections 5-8 off; no 24 bit address supplied by Multibus bd */
X	forceSw(set[sw], 5, FALSE);
X	forceSw(set[sw], 6, FALSE);
X	forceSw(set[sw], 7, FALSE);
X	forceSw(set[sw], 8, FALSE);
X    }
X
X    /* 24 bit DMA ? */
X    else if (dsiz == 24) {
X
X	/* No offset allowed */
X	if (doff != 0)
X	    err("Can't use DMA offset with 24 bit DMA", (char *) 0);
X
X	/* Switches are 1-4 off, 5-8 on to pass high bits unaltered */
X	forceSw(set[sw], 1, FALSE);
X	forceSw(set[sw], 2, FALSE);
X	forceSw(set[sw], 3, FALSE);
X	forceSw(set[sw], 4, FALSE);
X	forceSw(set[sw], 5, TRUE);
X	forceSw(set[sw], 6, TRUE);
X	forceSw(set[sw], 7, TRUE);
X	forceSw(set[sw], 8, TRUE);
X    }
X    else
X
X	/* Unknown DMA request */
X	err("DMA size must be either 20 or 24", (char *) 0);
X}
X
X/*
X * Set up the dip switch used for interrupt vector generation.  Should be
X * passed a -1 for vec if no vector is supplied by user; this forces all
X * switch sections off, we is the way we would be if user provided a PROM
X * for interrupt vector generation.
X */
Xstatic
XdoIntVec(vec, dip, set, sw)
Xlong vec;		/* Interrupt vector */
XdipSw dip[];		/* DIP switch descriptions */
Xshort set[][NSW];	/* Where the settings go */
Xint sw;			/* Switch # to set */
X{
X
X    /* Classic switch; just set it and return */
X    setSw(vec, &dip[sw], set[sw]);
X}
X
X/*
X * Set up switches for an address space;  address spaces are specified by
X * a two element array, the 0th element being the base address, and the 
X * next element the size.  Four switches are provided for setting; two
X * for the block size (order unimportant) and two for the base address
X * (order also unimportant).  If the block size is 0, the switch is set
X * for no response in that address space.  If block size is less than
X * minimum, it is set to minimum.  If it isn't a power of two, it is
X * rounded to the next highest power of two.
X */
Xstatic
XdoAddrSpace(space, minblk, dip, set, block1, block2, addr1, addr2)
Xlong space[];		/* Adress space description */
Xlong minblk;		/* Minimum blocksize allowed */
XdipSw dip[];		/* DIP switch descriptions */
Xshort set[][NSW];	/* Where the settings go */
Xint block1, block2;	/* Switches for block size */
Xint addr1, addr2; 	/* Switches for base address */
X{
X
X    /* Does board use this space ? */
X    if (space[1] != 0) {
X
X	/* Yes, make the block size a power of two, at least minblk */
X	makeValidBlockSize(&space[1], minblk);
X
X	/* Set the block size switches */
X	doBlockSize(space[1], &dip[block1], set[block1]);
X	doBlockSize(space[1], &dip[block2], set[block2]);
X
X	/* Set the base address switches */
X	doBaseAddr(space[0], space[1], &dip[addr1], set[addr1]);
X	doBaseAddr(space[0], space[1], &dip[addr2], set[addr2]);
X    }
X    else {
X
X	/* Address space not used, set switches for no response */
X	setSw((long) -1, &dip[block1], set[block1]);
X	setSw((long) -1, &dip[block2], set[block2]);
X	setSw((long) 0, &dip[addr1], set[addr1]);
X	setSw((long) 0, &dip[addr2], set[addr2]);
X    }
X}
X
X/*
X * Apply the algorithm for converting a block size to a number that the
X * switch setter can use.  The goal is to covert a number with a single
X * bit set (a legal block size) to a number with all of the bits that
X * are less than that bit set, and the original bit not set.  Sounds
X * like subtracting 1, doesn't it?
X */
Xstatic 
XdoBlockSize(size, dipswp, settings) 
Xlong size; 
XdipSw *dipswp;
Xboolean settings[];
X{
X
X    /* Apply the 'algorithm' */
X    size -= 1;
X
X    /* Set the switches */
X    setSw(size, dipswp, settings);
X}
X
X/*
X * Apply the algorithm for converting a block size and a base address
X * to a number that the switch setter can use.  The goal is to take the
X * blocksize (with one bit set), subtract one from it (thus setting
X * bits lower than the one set), then or'ing this number with the
X * base address.
X */
Xstatic 
XdoBaseAddr(base, size, dipswp, settings)
Xlong base, size;
XdipSw *dipswp;
Xboolean settings[];
X{
X    /* Apply the 'algorithm' */
X    base |= size - 1;
X
X    /* Set the switches */
X    setSw(base, dipswp, settings);
X}
X
X/*
X * Print out the dip switch settings in the order specified by an array of
X * ints indexing into the setting array.
X */
XshowSettings(set, nset, order)
Xboolean set[][NSW];
Xint nset;
Xint order[];
X{
X    int i;
X
X    for (i = 0; i < nset; i++)
X	dumpSw(set, order[i]);
X}
X
X/*
X * Given a switch number and the settings; print out the settings for
X * that switch.
X */
Xstatic
XdumpSw(set, sw)
Xboolean set[][NSW];	/* The array of switch settings */
Xint sw;			/* Switch # to be displayed */
X{
X    int i;
X
X    (void)printf("DIP%-2d: ", sw);
X    for (i = 0; i < NSW; i++) {
X	if (set[sw][i] == TRUE)
X	    (void)printf("%d ON   ", i + 1);
X	else if (set[sw][i] == FALSE)
X	    (void)printf("%d OFF  ", i + 1);
X	else
X	    (void)printf("%d X    ", i + 1);
X    }
X    (void)printf("\n");
X}
X
X/*
X * Force a section of an already chosen DIP switch to a particular
X * (ON, OFF, DONTCARE) value.  The section number is 1 through 8,
X * but the setting array is indexed 0 through 7.
X */
Xstatic 
XforceSw(settings, sw, how)
Xboolean settings[];
Xint sw;
Xboolean how;
X{
X
X    /* Force the setting */
X    settings[sw - 1] = how;
X}
X
X/*
X * Given a number and a description of how a dip switch is set (based on
X * which sections are affected by which bits of the number), set the dip
X * switch by putting the states of the switches in an array.
X */
Xstatic
XsetSw(val, dipswp, settings)
Xlong val;		/* Value to set in the switches */
XdipSw *dipswp;		/* Description of this switch */
Xboolean settings[];	/* Where the settings go */
X{
X    int i;
X    short bit;
X
X    /* Go through each section of the switch */
X    for (i = 0; i < NSW; i++) {
X
X	/* Is this position used (!= DONTCARE) ? */
X	if ((bit = dipswp->bits[i]) != DONTCARE) {
X
X	    /* Position used, is corresponding bit set in the input value? */
X	    if (val & (1 << bit))
X
X		/* Bit set, set the switch according to its sense */
X		settings[i] = dipswp->sense;
X	    else
X
X		/* Bit not set, set the switch opposite to its sense */
X		settings[i] = !dipswp->sense;
X	}
X	else
X
X	    /* Position not used, switch is a don't care, for now */
X	    settings[i] = DONTCARE;
X    }
X}
X
X/*
X * Convert a potential block size for a device into a valid one.  This
X * involves making sure that it is at least a minimum value (smallest that
X * can be set with the switches) and a power of 2.  If either does not hold,
X * the block size is rounded up and a warning sent to stderr.
X */
Xstatic
XmakeValidBlockSize(valp, minval)
Xlong *valp;		/* Pointer to number to be made valid */
Xlong minval;		/* Minimum block size allowable */
X{
X    long val = *valp;
X    int highest, lowest, i;
X
X    /* Block size must be at least minval */
X    if (val < minval) {
X
X	/* It wasn't; print a warning, set it to minimum and return */
X	(void)fprintf(stderr, 
X		      "Set blocksize of %ld to minimum allowable of %ld\n",
X		      val, minval);
X	*valp = minval;
X	return;
X    }
X
X    /* Find the highest bit set; and ensure that it's also the lowest */
X    highest = lowest = 0;
X
X    /* Cycle through the bits */
X    for (i = 1; i <= 32; i++) {
X
X	/* Bit set ? */
X	if (val & 1) {
X
X	    /* Bit is set, has lowest been detected ? */
X	    if (lowest == 0)
X
X		/* No, this is the lowest bit */
X		lowest = i;
X
X	    /* Bit is set, may be highest bit */
X	    highest = i;
X	}
X
X	/* Get to next bit */
X	val >>= 1;
X    }
X
X    /* Are highest and lowest the same bit ? */
X    if (highest != lowest) {
X
X	/* No, round up, print warning and return */
X	val = 1 << highest;
X	(void)fprintf(stderr, "Blocksize of %ld rounded up to %ld\n", 
X		      *valp, val);
X	*valp = val;
X    }
X}
X
X/* 
X * Print an error message with possibly one string argument.  Exit with
X * non-zero status.
X */
Xstatic 
Xerr(fmt, param)
Xchar *fmt, *param;
X{
X    (void)fprintf(stderr, fmt, param);
X    (void)fprintf(stderr, "\n");
X    exit(1);
X}
X
X/*
X * Given a string containing numbers in hex or decimal format, an array
X * to place their binary values in and a count, parse the string.  The
X * numbers are separated by commas.
X */
Xstatic
XgetVals(str, vals, count)
Xchar *str;		/* String containing the numbers */
Xlong *vals;		/* Array to place the values in */
Xint count;		/* Number of values to be parsed */
X{
X    long numParse();
X    char *p, *strtok();
X    char buf[255];
X
X    /* Make a copy of the string, since strtok() will trash it */
X    (void)strcpy(buf, str);
X
X    /* Parse it based on commas as separators */
X    p = strtok(buf, ",");
X
X    /* Cycle until end of string or we get all that we want */
X    while (p && count--) {
X
X	/* Extract the value, place it in the array */
X	*vals++ = numParse(p);
X
X	/* Advance to next possible number */
X	p = strtok((char *) 0, ",");
X    }
X
X    /* If we didn't parse the whole string, or didn't get all the values we
X       need, perhaps the user doesn't understand how we work */
X    if (p || count)
X	usage();
X}
X
X/*
X * Take a string, and extract either a hexadecimal or decimal number from it.
X * It is a fatal error if we can't get either.
X * Returns the value.
X */
Xstatic long
XnumParse(str)
Xchar *str;
X{
X    long scanval;
X    char foo[255];
X
X    /* Convert upper case to lower case (to parse 0x) */
X    lcase(str);
X
X    /* Scanf parsing; number may be hex, with no garbage afterward */
X    if (sscanf(str, "0x%lx%[^\1-\127]", &scanval, foo) == 1 ||
X
X    /* Or decimal, again with no garbage */
X        sscanf(str, "%ld%[^\1-\127]", &scanval, foo) == 1)
X        return(scanval);
X
X    /* Didn't grok it */
X    err("Cannot parse number: %s", str);
X    /*NOTREACHED*/
X}
X
X/*
X * Convert upper case characters in a string to lower case.
X */
Xlcase(p)
Xchar *p;
X{
X    for (; *p != '\0'; p++)
X	if (isupper(*p))
X	    *p = tolower(*p);
X}
XXX_EOF_XXX
if test 16196 -ne "`wc -c < vme2mb.c`"
then
    echo 'shar: transmission error on "vme2mb.c"'
fi
chk=`sum vme2mb.c | awk '{print $1}'`
if test 51222 -ne $chk -a 40552 -ne $chk
then
    echo 'shar: checksum error on "vme2mb.c"'
fi