[sci.electronics] SRAM Battery backup

gbell@pnet12.cts.com (Greg Bell) (05/14/89)

I'm working on a microcontroller project using Intel's 8031.  I have
a 1x16 LCD, a DTMF decoder, and a line voltage detector as peripherals.
 
I have a 2716 EPROM acting as a monitor ROM that loads a .OBJ file from
my AT clone's COM port into the static RAM.  Cuts software developement
time 4x at least! 
 
Alright, enough of the description...  I would like to battery back the
RAM.  Its a 6264LP-15.  I have the appropriate diodes set up so the
battery doesn't try to run the rest of the circuit, and so that it does
not get charged by the power supply.
 
How do I get the circuit to power down in an orderly fashion?  As of now,
the setup works many times, but occasionally the RAM gets corrupted. Its
possible that the RAM is getting corrupted on each power-up/down but just
becoming noticable when certain (instruction holding) locations get corr-
upted.
 
Is this RAM corruption due to a pulse on the WE/ (write enable) line?  Or,
is it because there's a program running in the RAM?  Do I have to jump
to some EPROM location before powering down just to assure the RAM isn't
selected?  
 
I've tried having the setup jump to an EPROM address so the RAM is quiet
at power down, and I still have the problem.  Also, I've tried buffering
the write line so that it can be pulled up when the power to the rest of
the circuit goes down.  Its hard to tell what's making a difference since
I can't tell what, if any, locations get corrupted at any one time.
 
I know Maxim and Dallas Semiconductor make chips that tame this problem, 
but I'm wondering if they're actually necessary.  Does anyone have Dallas
Semi's address?
 
I'd like suggestions on how to track down the actual problem (finding out
when the RAM is getting corrupted w/out printing out all RAM locations!)
and whether a solution can be arrived at without using an entire IC.
 

    Greg Bell_________________________________________________________
      Hardware hacker          |
      Electronics hobbyist     | UUCP:  uunet!serene!pnet12!gbell
      EE major at UC San Diego |

henry@utzoo.uucp (Henry Spencer) (05/15/89)

My understanding is that it's non-trivial to build circuitry that will
reliably keep a battery-backed RAM out of trouble during powerup and
powerdown.  If you want a solution quickly, buying one is probably simplest.

>I know Maxim and Dallas Semiconductor make chips that tame this problem, 
>but I'm wondering if they're actually necessary.  Does anyone have Dallas
>Semi's address?

Dallas Semi is at 4350 Beltwood Pkwy S, Dallas Texas 75244, (214)450-0400.
They make a nice gadget called a "smart socket" that is probably just what
you want -- it's a socket for a standard static RAM (actually there are
several depending on exactly what size of RAM) that includes batteries and
a switching circuit.  Use one of these instead of a standard socket and
you have battery backup.  They are not inordinately expensive.
-- 
Subversion, n:  a superset     |     Henry Spencer at U of Toronto Zoology
of a subset.    --J.J. Horning | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

johne@hpvcfs1.HP.COM (John Eaton) (05/16/89)

<<<< 
< Alright, enough of the description...  I would like to battery back the
< RAM.  Its a 6264LP-15.  I have the appropriate diodes set up so the
< battery doesn't try to run the rest of the circuit, and so that it does
< not get charged by the power supply.
----------
Thats the first step. You must also ensure that the CS2 is driven
DEEPLY into its inactive state during power down. By deeply I mean to
within 200 mv of the rails because the static current at Vih or Vil is
about 500X the current when driven to the rails. It is extremely important 
that you guarentee that the ram is not accessed during powerdown because
even a read will cause its current to jump by a factor of about 30,000.

When doing a battery backed design you must ensure that no signal from the
battery section is allowed to drive ANY unpowered device pin high. If you
do then the signal will attempt to power its VDD line through the input
protection diodes and you will see high current. If its a CMOS design then
the circuit may even still run. Power down the circuit and check the voltage
at the VDD pins of the chips that should be off. If you see 1 to 2 volts then
you have problems.

John Eaton
!hpvcfs1!johne

cook@stout.ucar.edu (Forrest Cook) (05/16/89)

In article <657@serene.UUCP> gbell@pnet12.cts.com (Greg Bell) writes:
>Alright, enough of the description...  I would like to battery back the
>RAM.  Its a 6264LP-15.

Call your MOSTEK sales rep and ask for their "zeropower rams".
They come in standard 2K X 8 and 8K X 8 packages with built in long life
lithium cells.

I have used a version that is a 2K X 8 chip with the last 8 bytes dedicated
to a built in real-time clock.  It has worked flawlessly for several years
and the battery is only drained when the power is off.  They offer a special
low power mode for storage.

I think one of the part numbers is MK48T02, I may be wrong on that one.

 ^   ^  Forrest Cook - Beware of programmers who carry screwdrivers - LB
/|\ /|\ cook@stout.ucar.edu (The preceeding was all my OPINION)
/|\ /|\ {husc6|rutgers|ames|gatech}!ncar!stout!cook
/|\ /|\ {uunet|ucbvax|allegra|cbosgd}!nbires!ncar!stout!cook

davidc@vlsisj.VLSI.COM (David Chapman) (05/16/89)

In article <657@serene.UUCP> gbell@pnet12.cts.com (Greg Bell) writes:
>How do I get the circuit to power down in an orderly fashion? ...
> 
>Is this RAM corruption due to a pulse on the WE/ (write enable) line? ... 
> 
>I've tried having the setup jump to an EPROM address so the RAM is quiet
>at power down, and I still have the problem.  Also, I've tried buffering
>the write line so that it can be pulled up when the power to the rest of
>the circuit goes down.  Its hard to tell what's making a difference since
>I can't tell what, if any, locations get corrupted at any one time.
> 
You really don't want to have anything running during the power-down
period.  If you can feed the "power down pending" signal to the non-
maskable interrupt line of the processor, and have it HALT, that might
reduce the incidence of the problem.  If you don't have advance notice
of power failure, then you need a power-fail-detection circuit and you
might as well buy the special chips.

You say that you pulled up the WE line during power-down.  Was the IC
that pulled this up also powered from the RAM battery?  If not, it might
get pulsed as power fails to the IC that pulls it up.  Very strange things
happen to digital ICs as the voltage drops towards 0.  This might be random
enough to explain your problems as well.  Think of what happens to the
processor as it loses power.  Do you want it to be running a program?  Nooo.

In fact, all of the circuitry that isolates the RAM must be powered by the 
battery.  If it's CMOS this won't be a power consumption problem.  Just make 
sure it runs at the voltage supplied by the battery (3V, I presume).

As Henry Spencer said, it might be easier just to buy one of the chips
designed for this purpose, but you should be able to do it yourself.
That's the hardware hacking spirit.

If you're really concerned about detecting RAM corruption problems during
power cycling, put a CRC routine into the power-down code.  This can be
done in a few (~50) lines of assembly language on most processors.  I have
public-domain C source code and (never-tested) 80x86 assembly language
that I can e-mail or post if you want.  BTW, the same code checks on power-up.

		David Chapman

{known world}!decwrl!vlsisj!fndry!davidc
vlsisj!fndry!davidc@decwrl.dec.com

jpexg@hermes.ai.mit.edu (John Purbrick) (05/16/89)

Recently I've seen huge (electrically, not physically) capacitors in the 
Digi-Key catalog. A whole farad! They have fairly high internal resistance
(~50 ohm) but to keep a trickle going into a memory chip, which is what they're
designed for, they should be OK. Whether something like this would do the job
depends on how long the system has to be kept alive without being plugged in, 
which would recharge the cap of course.

gbell@pnet12.cts.com (Greg Bell) (05/16/89)

johne@hpvcfs1.HP.COM (John Eaton) writes:
><<<< 
>< Alright, enough of the description...  I would like to battery back the
>< RAM.  Its a 6264LP-15.  I have the appropriate diodes set up so the
>< battery doesn't try to run the rest of the circuit, and so that it does
>< not get charged by the power supply.
>----------
>Thats the first step. You must also ensure that the CS2 is driven
>DEEPLY into its inactive state during power down. By deeply I mean to
>within 200 mv of the rails because the static current at Vih or Vil is
>about 500X the current when driven to the rails. It is extremely important 
>that you guarentee that the ram is not accessed during powerdown because
>even a read will cause its current to jump by a factor of about 30,000.
>

Really?  A read will cause problems?  In other words, I need to jump to a
location in the monitor EPROM before powering down (there's no way to get the 
8031 to just "HALT".  Maybe driving RESET high and holding it would do)?  

Would the Maxim/Dallas Semi monitor chips pull the CS2 inactive soon enough so
that the circuit could be doing anything at powerdown without trouble?  After
all, isn't that the purpose of the chip?

    Greg Bell_________________________________________________________
      Hardware hacker          |
      Electronics hobbyist     | UUCP:  uunet!serene!pnet12!gbell
      EE major at UC San Diego |

davidc@vlsisj.VLSI.COM (David Chapman) (05/18/89)

In article <662@serene.UUCP> gbell@pnet12.cts.com (Greg Bell) writes:
>johne@hpvcfs1.HP.COM (John Eaton) writes:
>>                                          ... It is extremely important 
>>that you guarentee that the ram is not accessed during powerdown because
>>even a read will cause its current to jump by a factor of about 30,000.
>
>Really?  A read will cause problems?  In other words, I need to jump to a
>location in the monitor EPROM before powering down (there's no way to get the 
>8031 to just "HALT".  Maybe driving RESET high and holding it would do)?  

The reason that power consumption rises is (as someone else already mentioned)
that you end up trying to power the rest of the circuit through the SRAM's
data lines.  This can do nasty things to your battery, like maybe make its
output voltage drop momentarily.  It could also damage the SRAM.

Remember, these circuits are not designed to work at 1-2 volts, even for a few
milliseconds as the circuit powers down.  You don't know what they will do.
Normally, you don't care because any state change is going to get lost when
power drops to 0.  Now you do care.  So in order to keep from frying or 
scrambling your SRAM, you must logically (if not electrically) isolate it from 
the rest of the circuit as soon as the power supply voltage starts to drop.
If you can keep the chip from being selected, that will prevent problems even
if the read line is being pulsed.

I hadn't thought of the CS2 line before (I _knew_ there were other control
lines besides the WR :-), but now I remember that some circuits (and chips)
use an active-high CS line as a battery backup isolater.  They put a 4.5V
sensor on it, and you tie it to Vdd (_not_ the battery).  As power drops the
SRAM cuts out.  This may be the salient feature of the SRAMs recommended
elsewhere, and might be included in the SRAM with its own battery on-board.  
Wrt the latter, I'd be worried if my system were to have a significant
lifetime (>3 years).  How do you know that the internal battery is dying
_before_ you lose data the next time you power down?

		David Chapman

{known world}!decwrl!vlsisj!fndry!davidc
vlsisj!fndry!davidc@decwrl.dec.com

nagle@well.UUCP (John Nagle) (05/18/89)

     I've used a New Micros M68HC11 microprocessor board with a "smart
socket" keeping the static RAM alive.  An interesting implication of this
is the behavior of the system when power is low.  I was powering the board
with a battery and a 7805 regulator.  When the battery voltage dropped
below about 6v, the output of the regulator fell from the regulated 5v
level.  This didn't bother the microprocessor, an all-CMOS device that will
run quite happily on 3v.  But the "smart socket" detected the loss of power
and took the RAM offline.  This caused the processor to crash and restart.
This M68HC11 had a Forth interpreter mask-programmed onto the chip, and
the Forth interpreter, at startup, searched RAM for a special pattern
indicating the start of the program to be run.  Not finding it, it simply
offered a Forth prompt to the terminal.  

     So, unexpectedly, the embedded application stopped running and the
Forth interpreter's prompt appeared, with a working Forth system available
using only the 1K of RAM on the M68HC11 chip, and the 32Kb on the "protected"
chip offline.  This puzzled us for a while.

					John Nagle

gbell@pnet12.cts.com (Greg Bell) (05/20/89)

 
 
 
Wow.  I've been overwhelmed with replies, suggestions, tips, etc.  Many
thanks to everybody that took the time to E-mail me.
 
I'm pretty much convinced that the main problem is that although I solved
the problem of leaving the WE line floating by using a pull up resistor,
the uP is tugging the WE low as it dies.
 
Many people recommended I purchase one of the RAM-in-a-socket contraptions.
I'd prefer not to do it this way since this project is a hacking experience
for me.   I think I'm going to go with one of the power monitor chips that
will disable the RAM's CS as soon as it sees the main power start to fall.
 
Yet another question, aimed at David C: if the power monitor chip pro-
tects the RAM by putting in in low power mode (disabling one of the CS lines),
will this solve the problem of the rest of the circuit getting power through
the data lines?  I assume this can only happen through output lines and not
input lines.  
 
If the CS is just yanked inactive, then it really shouldn't matter what
the processor is doing.  The processor can do any number of things as
the power drops anyway.  Things on the processor end are basically 
unpredictable, so the RAM has to be protected directly.  Seems that the
NMI, RESET, and HALT related solutions wouldn't work.
 
 
Thanks again, everybody... lets keep this conversation going!  Interesting
stuff.
 
 
 
 
 
(By the way, DavidC, could you send the CRC code you mentioned?  )
(and, to the guy who E-Mailed me from Arizona or New Mex about   )
(robotics... I lost your address and my reply bounced.  Try again)


    Greg Bell_________________________________________________________
      Hardware hacker          |
      Electronics hobbyist     | UUCP:  uunet!serene!pnet12!gbell
      EE major at UC San Diego |

davidc@vlsisj.VLSI.COM (David Chapman) (05/24/89)

In article <669@serene.UUCP> gbell@pnet12.cts.com (Greg Bell) writes:
>Yet another question, aimed at David C: if the power monitor chip pro-
>tects the RAM by putting in in low power mode (disabling one of the CS lines),
>will this solve the problem of the rest of the circuit getting power through
>the data lines?  I assume this can only happen through output lines and not
>input lines.  
Yes, the problem is driving through the output lines.  Disabling the RAM
should keep you out of trouble.
 
>(By the way, DavidC, could you send the CRC code you mentioned?  )
Coming up... (sorry it took so long to respond; we had a major network
upgrade last week).  It's all in the next post (those of you who don't
care are hereby warned).

		David Chapman

{known world}!decwrl!vlsisj!fndry!davidc
vlsisj!fndry!davidc@decwrl.dec.com

davidc@vlsisj.VLSI.COM (David Chapman) (05/24/89)

I've had a couple of requests for the CRC code that I mentioned.  So at the 
risk of offending the net gods by wasting all of their precious bandwidth, 
here it is (yes, I realize that it should be in comp.sources.whatever, but I
don't read them and I suspect few of you do).

If you're not interested in CRCs, type 'n' now.

There are four files encapsulated herein:  pdcrc.c, crc.h, crc.c, and crc.asm. 
Each is separated by a form feed and prefixed with a line of the form
"#file <file name>".  "pdcrc.c" is (I believe) a public domain set of CRC 
routines.  It came from an MS-DOS disk of C source code; wish I could remember 
where.  It's documented to the extreme.  Possible porting problem:  the author 
assumes that "short" is 16 bits; I've seen 8-bit shorts on some machines.  It
also assumes that adding a character to a long will be unsigned; some machines
perform signed character arithmetic.

Files crc.h and crc.c are the interface and implementation, respectively, of 
my version of the same code.  I changed the implementation from long integer 
registers to regular integers for speed (especially important on 16-bit 
processors that barely slog through 32-bit operations).  It's been a year 
since I worked on it, but I recall having tested it with the main() from 
pdcrc.c.  I recommend that you test it thoroughly yourself, of course.
Possible porting probems:  it assumes that integers are 16 bits (though it
should work regardless).  Itassumes that casting a character to an unsigned
int works (had a bug in a compler once that wouldn't allow that!).

File crc.asm is the _untested_ translation of crc.c to MASM for 80x86 
processors.  As you'll see pretty quickly, it won't even assemble yet (segment 
info is missing, etc.).  I think that the inner loop will work properly if you 
get the procedure shell around it, load the values properly, etc.  Note that 
the reference to "[00]" is a data segment load (I think - I'm not an expert 
80x86 programmer yet).  This was an exercise to see how easy and fast a CRC 
could be in assembly language, given a carry bit.  It's fewer lines, and 
generates an order of magnitude less code!  I bet it's at least one order of 
magnitude faster, too.

Files crc.c and crc.h are hereby released into the public domain for use as 
you see fit.  Since I can't prove that pdcrc.c is in the public domain 
(without more effort than my classes allow me right now), you may wish to use 
those instead.  Crc.asm is similarly released, but by the time you get it
working it won't be recognizable as my code anyway, so you're outof danger.

Remember:  you get what you paid for (don't sue me).

		David Chapman

{known world}!decwrl!vlsisj!fndry!davidc
vlsisj!fndry!davidc@decwrl.dec.com

#file pdcrc.c
/******************************************
*					  *
* Cyclic Redundancy Check (CRC) functions *
*					  *
******************************************/

/*
*   crc_clear:
*	This function clears the CRC to zero. It should be called prior to
*	the start of the processing of a block for both received messages,
*	and messages to be transmitted.
*
*	Calling sequence:
*
*	short crc;
*	crc = crc_clear();
*/
short crc_clear()
{
	return(0);
}
/*
*   crc_update:
*	this function must be called once for each character which is
*	to be included in the CRC for messages to be transmitted.
*	This function is called once for each character which is included
*	in the CRC of a received message, AND once for each of the two CRC
*	characters at the end of the received message. If the resulting
*	CRC is zero, then the message has been correctly received.
*
*   Calling sequence:
*
*	crc = crc_update(crc,next_char);
*/
short crc_update(crc,crc_char)
short crc;
char crc_char;
{
	long x;
	short i;

/* "x" will contain the character to be processed in bits 0-7 and the CRC    */
/* in bits 8-23. Bit 24 will be used to test for overflow, and then cleared  */
/* to prevent the sign bit of "x" from being set to 1. Bits 25-31 are not    */
/* used. ("x" is treated as though it is a 32 bit register).                 */
	x = ((long)crc << 8) + crc_char;    /* Get the CRC and the character */

/* Repeat the following loop 8 times (for the 8 bits of the character).      */
	for(i = 0;i < 8;i++)
	{

/* Shift the high-order bit of the character into the low-order bit of the   */
/* CRC, and shift the high-order bit of the CRC into bit 24.		     */
		x = x << 1;			   /* Shift "x" left one bit */

/* Test to see if the old high-order bit of the CRC was a 1.		     */
		if(x & 0x01000000)		       /* Test bit 24 of "x" */

/* If the old high-order bit of the CRC was a 1, exclusive-or it with a one  */
/* to set it to 0, and exclusive-or the CRC with hex 1021 to produce the     */
/* CCITT-recommended CRC generator of: X**16 + X**12 + X**5 + 1. To produce  */
/* the CRC generator of: X**16 + X**15 + X**2 + 1, change the constant from  */
/* 0x01102100 to 0x01800500. This will exclusive-or the CRC with hex 8005    */
/* and produce the same CRC that IBM uses for their synchronous transmission */
/* protocols.								     */
			x = x ^ 0x01102100;	/* Exclusive-or "x" with a...*/
					      /* ...constant of hex 01102100 */
/* And repeat 8 times.							     */
	}						/* End of "for" loop */

/* Return the CRC as the 16 low-order bits of this function's value.         */
	return(((x & 0x00ffff00) >> 8)); /* AND off the unneeded bits and... */
				  /* ...shift the result 8 bits to the right */

}
/*
*   crc_finish:
*	This function must be called once after all the characters in a block
*	have been processed for a message which is to be TRANSMITTED. It
*	returns the calculated CRC bytes, which should be transmitted as the
*	two characters following the block. The first of these 2 bytes
*	must be taken from the high-order byte of the CRC, and the second
*	must be taken from the low-order byte of the CRC. This routine is NOT
*	called for a message which has been RECEIVED.
*
*   Calling sequence:
*
*	crc = crc_finish(crc);
*/
short crc_finish(crc)
short crc;
{
/* Call crc_update twice, passing it a character of hex 00 each time, to     */
/* flush out the last 16 bits from the CRC calculation, and return the	     */
/* result as the value of this function.				     */
	return(crc_update(crc_update(crc,'\0'),'\0'));

}

/*
* This is a sample of the use of the CRC functions, which calculates the
* CRC for a 1-character message block, and then passes the resulting CRC back
* into the CRC functions to see if the "received" 1-character message and CRC
* are correct.
*/
main()
{

	short crc;				       /* The calculated CRC */
	char crc_char;				  /* The 1-character message */
	char x, y;	      /* 2 places to hold the 2 "received" CRC bytes */

	crc_char = 'A';                    /* Define the 1-character message */
	crc = crc_clear();	/* Reset the CRC to "transmit" a new message */
	crc = crc_update(crc,crc_char);   /* Update the CRC for the first... */
				   /* ...(and only) character of the message */
	crc = crc_finish(crc);	      /* Finish the transmission calculation */
	x = (char)((crc & 0xff00) >> 8);  /* Extract the high-order CRC byte */
	y = (char)(crc & 0x00ff);	   /* And extract the low-order byte */
	printf("%04x\n",crc);                           /* Print the results */

	crc = crc_clear();		   /* Prepare to "receive" a message */
	crc = crc_update(crc,crc_char);   /* Update the CRC for the first... */
				   /* ...(and only) character of the message */
	crc = crc_update(crc,x);     /* Pass both bytes of the "received"... */
	crc = crc_update(crc,y);	   /* ...CRC through crc_update, too */
	printf("%04x\n",crc);    /* If the result was 0, then the message... */
					    /* ...was received without error */

}


#file crc.h

/* crc.h - interface for 16-bit CRC routines */

/* I assume two-byte integers. */

typedef unsigned int CRCSHORT;          /* return value type */

#ifndef UNIX                            /* no function prototyping in UNIX C */

CRCSHORT crcupdate(char,CRCSHORT);      /* update the CRC with the given */
                                        /* 8-bit value. */

CRCSHORT crcval(CRCSHORT);              /* return the final CRC, given */
                                        /* working value so far */

CRCSHORT crcstr(char *);                /* compute CRC value of string */

CRCSHORT crcstrn(char *,int);           /* compute CRC value of "len" chars */

#else UNIX

CRCSHORT crcupdate(),crcval(),crcstr(),crcstrn();

#endif

/* usage:

   char *p;
   CRCSHORT crc;

   crc = 0;
   while (*p)
       crc = crcupdate(*p++,crc);
   crc = crcval(crc);

   append "crc" to the string and transmit the entire thing. at the receiving
   end, pass each character (including the trailing CRC bytes) through the
   crcupdate() function again, and 0x0000 should result. if not, something
   got corrupted.

   this particular code example is how "crcstr" is implemented.

*/

#file crc.c
/* crc.c - 16-bit cyclic redundancy check */

/* uses the CCITT-recommended function X**16 + X**12 + X**5 + 1. */

/* in the polynomial definition below, the uppermost bit is assumed. this */
/* allows the use of shorter numbers than otherwise would be required. */

#define CRC16POLY 0x1021                /* CCITT-16 polynomial in hex */

#include "crc.h"

CRCSHORT crcupdate(ch,crc)              /* update the CRC with the given */
char ch;                                /* 8-bit value. */
CRCSHORT crc;
{
    /* for speed all of this should really be done in assembly language */
    /* judicious use of the carry flag would make the temporary flags */
    /* superfluous). */

    int i;
    register unsigned c,bit,exor;

    c = (unsigned) ch;               

    for (i = 0; i < 8; ++i) {
        bit = (c > 0x7f);               /* 1 or 0 from high bit */
        c = (c << 1) & 0xff;
        exor = (crc & 0x8000);          /* just non-zero is OK here */
        crc = (crc << 1) | bit;
        if (exor)                       /* recirculation... */
            crc ^= CRC16POLY;
    }  /* end of for (i) */

    return(crc);

}  /* end of crcupdate() */

CRCSHORT crcval(crc)                    /* return the current CRC, given */
CRCSHORT crc;                           /* working value so far */
{
    /* we pass 0x0000 through, and the caller appends the resulting CRC */
    /* to its byte stream. at the other end the final CRC of the stream */
    /* (including the CRC passed in) should be 0x0000. */

    crc = crcupdate('\0',crc);
    return(crcupdate('\0',crc));

}  /* end of crcval() */

CRCSHORT crcstr(str)                    /* compute CRC of '\0' terminated */
char *str;                              /* string (not including '\0') */
{
    CRCSHORT crc;

    crc = 0;
    while (*str)
        crc = crcupdate(*str++,crc);
    return(crcval(crc));

}  /* end of crcstr() */

CRCSHORT crcstrn(str,len)               /* compute CRC of "len" chars of str */
char *str;
int len;
{
    int i;
    CRCSHORT crc;

    crc = 0;
    for (i = 0; i < len; ++i)
        crc = crcupdate(*str++,crc);
    return(crcval(crc));

}  /* end of crcstrn() */


#file crc.asm
;
;       CRCUPDATE - run a character through the CRC generator
;
;       hand optimized for the 8086, with C code for you machine-
;       independent types (several times slower! optimizing compilers
;       usually can't tell that the binary flags "bit" and "exor" are
;       really carry flag hacks...).
;
;       #define CRC16POLY 0x1021
;
;       void crcupdate(ch)
;       char ch;
;       {
;           int i;
;           register unsigned c,bit,exor;
;
;           for (i = 8; i; --i) {
;               bit = (c > 0x7f);
;               c = (c << 1) & 0xff;
;               exor = (_crc16 & 0x8000);
;               _crc16 = (_crc16 << 1) | bit;
;               if (exor)
;                   _crc16 ^= CRC16POLY;
;           }  /* end of for (i) */
;
;       }  /* end of crcupdate() */
'
crcupdate:
        mov     ax,[00]                 ;get _crc16
        mov     bl,6[bp]                ;c = (unsigned) ch;
        mov     cx,8                    ;for (i = 8; i; --i)
shiftloop:                              ;{
        shl     bl,1                    ;    bit = (c < 0x7f);
                                        ;    c = (c << 1) & 0xff;
        rcl     ax,1                    ;    exor = (_crc16 & 0x8000);
                                        ;    _crc16 = (_crc16 << 1) | bit;
        jnc     noexor                  ;    if (exor)
        xor     ax,1021H                ;        _crc16 ^= CRC16POLY;
noexor: loop    shiftloop               ;}
        mov     [00],ax                 ;save _crc16
        ;assorted stack manipulation...
        ret