/*****************************************************************************/ /* */ /* random.c ;pseudo-random number generator functions for IC */ /* ;works with both the 6.270 board and the Handy Board */ /* */ /* by */ /* */ /* Dr. Richard F. Drushel */ /* Department of Biology */ /* Case Western Reserve University */ /* Biology 300 */ /* 2080 Adelbert Road */ /* Cleveland, Ohio 44106-7080 U.S.A. */ /* rfd@po.cwru.edu */ /* */ /*****************************************************************************/ /* */ /* VERSION HISTORY: */ /* */ /* Whenever the program is updated, record it here. Add new version info */ /* to the top of this list, so the newest version is always first. */ /* */ /* 1.2 18 May 1997 ;Richard F. Drushel */ /* ;comment style standardization */ /* ;also incorporates random() function from */ /* ;Fred Martin's lib_hb.c for the Handy Board */ /* */ /* 1.1 24 April 1997 ;David F. Meeker */ /* ;ECMP 375, Spring 1996, CWRU */ /* ;rnd() and randomize() algorithms made */ /* ;thread-safe */ /* */ /* 1.0 24 August 1996 ;Richard F. Drushel */ /* ;initial code and rnd() algorithm */ /* ;uses resource locking because state machine */ /* ;is non-reentrant */ /* */ /*****************************************************************************/ /* */ /* PUBLIC FUNCTIONS: */ /* */ /* These functions may be used freely by user programs. */ /* */ /* int rnd(int arg) ;returns pseudo-random number */ /* ;(16-bit signed int) based on value of arg */ /* ;arg==0 returns last random number (for debug) */ /* ;arg>0 returns random number */ /* ;arg<0 reseeds random number generator */ /* with default seeds and returns */ /* the first number in that series */ /* ;RFD invented the algorithm, and it's not */ /* ;perfect, but in testing it looks reasonably */ /* ;random. */ /* ;Note: rnd() is *NOT* reentrant!! */ /* ;it sets a busy flag _RND_BUSY if another */ /* ;process is currently calling rnd(). */ /* ;made thread-safe by DFM 4/24/1997 */ /* */ /* void randomize(int seed) ;reseeds random number generator to user- */ /* ;defined seed */ /* ;non-reentrant, so waits until busy flag is */ /* ;cleared. */ /* ;made thread-safe by DFM 4/27/1997 */ /* */ /* int random(int mod) ;random integers from peeking at 2 MHZ system */ /* ;clock. mod is a modulo value which can range */ /* ;2 to 32767. */ /* ;code by Fred G. Martin from lib_hb.c */ /* */ /*****************************************************************************/ /* */ /* PUBLIC GLOBAL VARIABLES: */ /* */ /* These global variables may be accessed freely by user programs. Any */ /* restrictions on their use (e.g., flags which are read-only) should be */ /* noted here. It is suggested that global variables be named in all */ /* capital letters, to avoid confusion with local varibles which are */ /* defined within functions. */ /* */ /* None. */ /* */ /*****************************************************************************/ /* */ /* PRIVATE FUNCTIONS: */ /* */ /* These are internal functions which have no public entry points, and */ /* which user programs should not access directly. It is suggested that */ /* private functions be named with a leading underscore (_), to avoid */ /* confusion with user-defined functions which might have the same name. */ /* */ /* int _rotate(int value, int direction, int rotcount) ;rotate bits */ /* */ /* void _rnd() ;actually do the pseudo-random number algorithm */ /* */ /*****************************************************************************/ /* */ /* PRIVATE GLOBAL VARIABLES: */ /* */ /* These are internal global variables which user programs should not */ /* access directly. It is suggested that private global variables be */ /* named in all capital letters with a leading underscore (_), to avoid */ /* confusion with user-defined global variables which might have the same */ /* name. */ /* */ /* int _RND_BUSY ;busy flag since rnd() is *NOT* reentrant!! */ /* int _SEED1 ;lobyte of seed (only uses lower 8 bits) */ /* int _SEED2 ;hibyte of seed (only uses lower 8 bits) */ /* int _ROTATE1COUNT ;working bit rotate count, seed 1 */ /* int _ROTATE1DIR ;working rotate direction, seed 1 (0=left, */ /* ;1=right) */ /* int _ROTATE2COUNT ;working bit rotate count, seed 2 */ /* int _ROTATE2DIR ;working rotate direction, seed 2 (0=left, */ /* ;1=right) */ /* int _RNDMASK ;bit mask for both seeds */ /* int _NHITS ;how many times we've done a certain task */ /* int _CRITICAL ;threshold value for resetting _NHITS */ /* int _RNDFUDGE ;fudge value */ /* int _ODD_OR_EVEN ;whether to return the next rnd or to throw it */ /* ;away */ /* int _AM_ERSTEN ;flag for whether a random number has been */ /* ;obtained since the last rnd(-1) call. This */ /* ;lets rnd(-1) return the same value as the very */ /* ;first call to rnd(1). */ /* */ /*****************************************************************************/ /* */ /* EXTERNAL LIBRARY FILE DEPENDENCIES: */ /* */ /* Any external functions or variables which are used but not defined in */ /* this file should be noted here. List the external library filename, */ /* the library function, and the local calling function. */ /* */ /* math.c ;uf2int() used by rnd() */ /* */ /*****************************************************************************/ /*****************************************************************************/ /* */ /* global variable declarations */ /* */ /*****************************************************************************/ int _SEED1=0x0AA; int _SEED2=0x055; int _ROTATE1COUNT=1; int _ROTATE1DIR=0; int _ROTATE2COUNT=3; int _ROTATE2DIR=1; int _RNDMASK=0x0F0; int _NHITS=0; int _CRITICAL=512; /* an empirical value that seems to work well */ int _RNDFUDGE=0; int _ODD_OR_EVEN=0; int _AM_ERSTEN=1; /* set our initial use flag */ int _RND_BUSY=0; /* initial state is free */ /*****************************************************************************/ /* */ /* function declarations */ /* */ /*****************************************************************************/ void randomize(int seed) /* reseed random number generator to user- */ /* defined value */ /* not reentrant; looks at _RND_BUSY flag and */ /* waits until the flag is clear */ /* DFM 4/24/97: Fixed problem where, if a */ /* process was swapped out while holding the */ /* lock, then killed, all following calls to */ /* rnd() or randomize() would hang the process */ /* forever. */ { float f; while (_RND_BUSY==1) { /* wait until busy flag clears */ defer(); /* DFM 4/24/97: The lock won't be returned */ /* by this process, so swap it out. */ } hog_processor(); /* DFM 4/24/97: Get a timeslice long enough to */ /* guarantee that no multitasking will occur */ /* while we have the lock. */ _RND_BUSY=1; /* lock the generator */ _SEED1=seed&0x0FF; /* get lobyte of seed */ f=(float)(seed); if (f<0.) f=f+65536.; _SEED2=(int)(f/256.); /* kludge to get hibyte of seed */ /* necessary because dear old IC won't do */ /* -1/256 and get 255 the way it should :-( */ _RND_BUSY=0; /* free the generator for other processes */ defer(); /* DFM 4/24/97: Defer remainder of timeslice. */ } /*****************************************************************************/ int _rotate(int value, int direction, int rotcount) /* internal routine which rotates bits */ /* on entry, a=value to be rotated, */ /* b=direction (0=left, 1=right), and */ /* c=number of times to rotate it */ /* Note: has fudge so that rotcount=0 */ /* always results in 1 rotation. This */ /* is needed to break the symmetry of */ /* the algorithm; otherwise you get */ /* fractal patterns in the output. */ { int n,mybit0; if (rotcount==0) rotcount++; /* fudge */ if (direction==0) { for (n=0;n0) /* we had a carry into bit 8 */ { value=value|1; /* so copy into bit 0 */ value=value&255; /* and wipe bits 8-15 */ } } } else { for (n=0;n0) /* if we had a carry, */ value=value|128; /* put it into bit 7 */ } } return(value); /* at wast, at wast, the scwewwy wabbits awe done! */ } /*****************************************************************************/ void _rnd() /* internal routine which does all the work */ /* requires that the global variables be already set up */ /* changes the value of the global variables! */ { if ((_SEED1&1)>0) /* set rotate2dir */ _ROTATE2DIR=1; /* bit 0 was set */ else _ROTATE2DIR=0; if ((_SEED2&8)>0) /* set rotate1dir */ _ROTATE1DIR=1; /* bit 3 was set */ else _ROTATE1DIR=0; _ROTATE2COUNT=(_ROTATE1COUNT^_SEED2)&7; /* keep it 0-7 */ _ROTATE1COUNT=(_ROTATE2COUNT&_SEED1)&7; /* keep it 0-7 */ _SEED1=_rotate(_SEED1,_ROTATE1DIR,_ROTATE1COUNT); /* rotate the seed 1 bits */ _SEED2=_rotate(_SEED2,_ROTATE2DIR,_ROTATE2COUNT); /* rotate the seed 2 bits */ _RNDMASK-=_SEED2; if (_RNDMASK<0) _RNDMASK+=256; /* keep it a positive number */ _RNDMASK-=_RNDFUDGE; if (_RNDMASK<0) _RNDMASK+=256; /* keep it a positive number */ _RNDMASK-=_SEED1; if (_RNDMASK<0) _RNDMASK+=256; /* keep it a positive number */ _SEED1=_SEED2^_RNDMASK; _SEED2=_SEED1&_RNDMASK; _RNDMASK=(255-_RNDMASK)&255; /* 1's complement, keeping only lower 8 bits */ _NHITS++; /* one more hit */ if (_NHITS==_CRITICAL) /* is it the magic threshold? */ { _RNDFUDGE=(_RNDFUDGE+1)&255; /* keep only lower 8 bits */ _NHITS=0; /* and reset the hit parade */ } } /*****************************************************************************/ int rnd(int arg) /* pseudo-random number generator */ /* this is the public entry point */ /* not reentrant; looks at _RND_BUSY flag and */ /* waits until the flag is clear */ /* DFM 4/24/97: Fixed problem where, if a */ /* process was swapped out while holding the */ /* lock, then killed, all following calls to */ /* rnd() or randomize() would hang the process */ /* forever. */ { float s1,s2; /* needed to pass final arguments */ while (_RND_BUSY==1) { /* wait until busy flag is clear */ defer(); /* DFM 4/24/97: The lock won't be returned */ /* by this process, so swap it out. */ } hog_processor(); /* DFM 4/24/97: Get a timeslice long enough to */ /* guarantee that no multitasking will occur while */ /* we have the lock. */ _RND_BUSY=1; /* lock the generator */ if (arg>0) { if (_AM_ERSTEN==1) { /* do nothing, just return the existing random number */ } else /* do the whole shebang */ { if (_ODD_OR_EVEN==1) /* if it's an odd time */ { _ODD_OR_EVEN=0; /* flag that we're even */ _rnd(); /* get a random number and throw it away */ } else /* we're even */ _ODD_OR_EVEN=1; /* make us odd for next time */ _rnd(); /* now get a random number and keep it */ } } else { if (arg<0) { /* restore default values for our globals */ _SEED1=0x0AA; _SEED2=0x055; _ROTATE1COUNT=1; _ROTATE1DIR=0; _ROTATE2COUNT=3; _ROTATE2DIR=1; _RNDMASK=0x0F0; _NHITS=0; _CRITICAL=512; /* even though we don't change this, */ /* put it back for safety */ _RNDFUDGE=0; _ODD_OR_EVEN=0; } else /* (arg==0) */ { /* there's nothing for us to do, */ /* just return the existing random number */ } } _AM_ERSTEN=0; /* mark that we've been here */ s1=int2uf(_SEED1); /* get our seeds as floats */ s2=int2uf(_SEED2); _RND_BUSY=0; /* free the generator */ defer(); /* DFM 4/24/97: Defer remainder of timeslice. */ return (uf2int(s1+(256.*s2))); /* make the final signed integer */ } /*****************************************************************************/ int random(int mod) /* random integers from peeking at the */ /* 2 MHZ system clock. */ /* mod ranges from 2 to 32767. */ /* original code by Fred G. Martin */ { return (peekword(0x100e) & 0x7fff) % mod; } /*****************************************************************************/