|
Programming hacks for the PDP10/DECSYSTEM-20. The first 36
hacks are taken verbatim from:
HAKMEM - Artificial Intelligence Memo No. 239
Massachusetts Institute of Technology A. I. Laboratory
M. Beeler, R. W. Gosper & R. Schroeppel, February 29,1972
Items 1 - 36 correspond to items 145 - 180 (pages 72 - 86)
in the original document.
Page 2
WARNING: Numbers in this section are octal (and occasionally binary)
unless followed by a decimal point.
105=69.. (And 105.=69 hexadecimal.)
ITEM 1 (Gosper):
Proving that short programs are neither trivial nor exhausted yet,
there is the following:
0/ TLCA 1,1(1)
1/ see below
2/ ROT 1,9
3/ JRST 0
This is a display hack (that is, it makes pretty patterns) with the
low 9 bits = Y and the 9 next higher = X; also, it makes interesting,
related noises with a stereo amplifier hooked to the X and Y signals.
Recommended variations include:
CHANGE: GOOD INITIAL CONTENTS OF 1:
none 377767,,377767; 757777,,757757; etc.
TLC 1,2(1) 373777,,0; 300000,,0
TLC 1,3(1) -2,,-2; -5,,-1; -6,,-1
ROT 1,1 7,,7; A0000B,,A0000B
ROTC 1,11 ;Can't use TLCA over data.
AOJA 1,0
ITEM 2:
Another simple display program: ("munching squares")
It is thought that this was discovered by Jackson Wright on the RLE
PDP-1 circa 1962.
DATAI 2
ADDB 1,2
ROTC 2,-22
XOR 1,2
JRST .-4
2=X, 3=Y. Try things like 1001002 in data switches. This also does
interesting things with operations other than XOR, and rotations other
than -22. (Try IOR; AND; TSC; FADR; FDV(!); ROT -14, -9, -20,
...)
ITEM 3 (Schroeppel):
Munching squares is just views of the graph Y = X XOR T for
consecutive values for T = time.
ITEM 4 (Cohen, Beeler):
Page 3
A modification to munching squares which reveals them in frozen states
through opening and closing curtains: insert FADR 2,1 before the XOR.
Try data switches =
4000,,4 1000,,2002 2000,,4 0,,1002
(Notation: <left half>,,<right half>
Also try the FADR after the XOR, switches = 1001,,1.
ITEM 5 (Minsky):
Here is an elegant way to draw almost circles on a point-plotting
display. CIRCLE ALGORITHM:
NEW X = OLD X - e * OLD Y
NEW Y = OLD Y + e * NEW(!) X
This makes a very round ellipse centered at the origin with its size
determined by the initial point. e determines the angular velocity of
the circulating point, and slightly affects the eccentricity. If e is
a power of 2, then we don't even need multiplication, let alone square
roots, sines, and cosines! The "circle" will be perfectly stable
because the points soon become periodic.
The circle algorithm was invented by mistake when I tried to save one
register in a display hack! Ben Gurley had an amazing display hack
using only about six or seven instructions, and it was a great wonder.
But it was basically line-oriented. It occurred to me that it would
be exciting to have curves, and I was trying to get a curve display
hack with minimal instructions.
ITEM 6 (Schroeppel):
PROBLEM: Although the reason for the circle algorithm's stability is
unclear, what is the number of distinct sets of radii? (Note:
algorithm is invertible, so all points have predecessors.)
ITEM 7 (Gosper):
Separating X from Y in the above recurrence,
X(N+1) = (2-e^2)*X(N) - X(N-1)
Y(N+1) = (2-e^2)*Y(N) - Y(N-1).
These are just the Chebychev recurrence with cos A (the angular
increment) = 1-(e^2)/2. Thus X(N) and Y(N) are expressible in the
form R cos(N A + B). The B's and R for X(N) and Y(N) can be found
from N=0,1. The B's will differ by less than Pi/2 so that the curve
is not really a circle. The algorithm is useful nevertheless, because
it needs no sine or square root function, even to get started.
X(N) and Y(N) are also expressible in closed form in the algebra of
Page 4
ordered pairs described under linear recurrences, but they lack the
remarkable numerical stability of the "simultaneous" form of the
recurrence.
ITEM 8 (Salamin):
With exact arithmetic, the circle algorithm is stable if |e| < 2. In
this case, all points lie on the ellipse
X^2 - e * X * Y + Y^2 = constant,
where the constant is determined by the initial point. This ellipse
has its major axis at 45 degrees (if e > 0) or 135 degrees (if e < 0)
and has eccentricity
(e/(1 + e/2)^2.
ITEM 9 (Minsky):
To portray a 3-dimensional solid on a 2-dimensional display, we can
use a single circle algorithm to compute orbits for the corners to
follow. The (positive or negative) radius of each orbit is determined
by the distance (forward or backward) from some origin to that corner.
The solid will appear to wobble rigidly about the origin, instead of
simply rotating.
ITEM 10 (Gosper):
The myth that any given programming language is machine independent is
easily exploded by computing the sum of powers of 2.
If the result loops with period = 1 with sign +,
you are on a sign-magnitude machine.
If the result loops with period = 1 at -1,
you are on a twos-complement machine.
If the result loops with period > 1, including the beginning,
you are on a ones-complement machine.
If the result loops with period > 1, not including the beginning,
your machine isn't binary--the pattern should tell you the base.
If you run out of memory, you are on a string or Bignum system. If
arithmetic overflow is fatal error, some fascist pig with a read-only
mind is trying to enforce machine independence. But the very ability
to trap overflow is machine dependent.
By this strategy, consider the universe, or, more precisely algebra:
let X = the sum of many powers of two = ... 111111
now add X to itself; X + X = ...111110
Page 5
thus, 2X = X - 1 so X = -1
therefore algebra is run on a machine (the universe)
which is twos-complement.
ITEM 11 (Liknaitzky):
To subtract the right half of an accumulator from the left (as in
restarting an AOBJN counter):
IMUL A,[377777,,1]
ITEM 12 (Mitchell):
To make an AOBJN pointer when the origin is fixed and the length is a
variable in A:
HRLOI A,-1(A)
EQVI A,ORIGIN
ITEM 13 (Freiberg):
If instead, A is a pointer to the last word
HRLOI A,-ORIGIN(A)
EQVI A,ORIGIN
Slightly faster: change the HRLOIs to MOVSIs and the EQVI addresses
to -ORIGIN-1. These two routines are clearly adjustable for BLKOs and
other fenceposts.
ITEM 14 (Gosper, Salamin, Schroeppel):
A miniature (recursive) sine and cosine routine follows.
COS: FADR A,[1.57079632679] ;Pi/2
SIN: MOVM B,A ;argument in A
CAMG B,[.00017] ;< 3^(-3) / 2^(13)
POPJ P, ;sin X = X, within 27. bits
FDVRI A,(-3.0)
PUSHJ P,SIN ;sin -X/3
FMPR B,B
FSC B,2
FADRI B,(-3.0)
FMPRB A,B ;sin X = 4(sin -X/3)^3 -3(sin -X/3)
POPJ P, ;sin in A, sin or |sin| in B
;|sin| in B occurs when angle is smaller than end test
Changing both -3.0's to +3.0's gives sinh:
sinh X = 3 sinh X/3 + 4 (sinh X/3)^3.
Page 6
Changing the first -3.0 to a +9.0, then inserting PUSHJ P,.+1 after
PUSHJ P,SIN gains about 20% in speed and uses half the pushdown space
(< 5 levels in the first 4 quadrants). PUSHJ P,.+1 is a nice way to
have something happen twice. Other useful angle multiplying formulas
are:
tanh X = (2 tanh X/2)/(1 + (tanh X/2)^2)
tan X = (2 tan X/2)/(1 - (tan X/2)^2)
if infinity is handled correctly. For cos and cosh, one can use:
cos X = 1 - 2 (sin X/2)^2, cosh X = 1 + 2 (sinh X/2)^2.
In general, to compute functions like e^X, cos X, elliptic functions,
etc. by iterated application of double and triple argument formulas,
it is necessary to subtract out the constant in the Taylor series and
transform the range reduction formula accordingly. Thus:
F(X) = cos(X)-1 F(2X) = 2F*(F+2) F(e) = -e^2/2
G(X) = e^X-1 G(2X) = G*(G+2) G(e) = e
This is to prevent the destruction of the information in the
range-reduced argument by the addition of a quantity near 1 upon the
success of the e test. The addition of such a quantity in the actual
recurrences is OK since the information is restored by the multiply.
In fact, a cheap and dirty test for F(e) sufficiently small is to see
if the addition step has no effect. People lucky enough to have a
square root instruction can get natural log by iterating
X <- X/((1+X)^2 + 1) until 1+X = 1.
Then multiply by 2^(number of iterations). Here, a LSH or FSC would
work.
ITEM 15 (Gosper, Schroeppel):
(Numbers herein are decimal.)
The correct epsilon test in such functions as the foregoing SIN are
generally the largest argument for which addition of the second term
has no effect on the first. In SIN, the first term is x and the
second is -(x^3)/6, so the answer is roughly the x which makes the
ratio of those terms (1/(2^27); so x = (3^(-3))/(2^(13)). But this
is not exact, since the precise cutoff is where the neglected term is
the power of 2 whose 1 bit coincides with the first neglected (28th)
bit of the fraction. Thus, (x^3)/6 = 1/(2^(27)) * 1/(2^(13)), so x =
(3^(-3)) / (2^(13).
ITEM 16 (Gosper):
Here is a way to get log base 2. A and B are consecutive. Call by
PUSHJ P,LOG2 with a floating point argument in A.
LOG2: LSHC A,-33
MOVSI C,-201(A)
Page 7
TLC C,211000 ;Speciner's bum
MOVEI A,200 ;exponent and sign sentinel
LOGL: LSH B,-9
REPEAT 7, FMPR B,B ;moby flunderflo
LSH B,2
LSHC A,7
SOJG A,LOGL ;fails on 4th try
LSH A,-1
FADR A,C
POPJ P, ;answer in A
Basically, you just square seven times and use the low seven bits of
the exponent as the next seven bits of the log.
ITEM 17 (Gosper):
To swap the contents of two locations in memory:
EXCH A,LOC1
EXCH A,LOC2
EXCH A,LOC1
Note: LOC1 must not equal LOC2! If this can happen use
MOVE-EXCH-MOVEM, clobbering A.
ITEM 18 (Gosper):
To swap two bits in an accumulator:
TRCE A,BITS
TRCE A,BITS
TRCE A,BITS
Note (Nelson): last TRCE never skips, and used to be TRC, but TRCE is
less forgettable. Also, use TLCE or TDCE if the bits are not in the
right half.
ITEM 19 (Sussman):
To exchange two variables in LISP without using a third variable:
(SETQ X (PROG2 O Y (SETQ Y X)))
ITEM 20 (Samson):
To take MAX in A of two byte pointers (where A and B are consecutive
accumulators):
ROTC A,6
CAMG A,B
EXCH A,B
Page 8
ROTC A,-6
ITEM 21 (Freiberg):
A byte pointer can be converted to a character address < 2^(18) by:
MULI A,<# bytes/word>
SUBI B,1-<# b/w>(A)
To get full word character address, use SUB into magic table.
ITEM 22 (Gosper, Liknaitzky):
To rotate three consecutive accumulators N < 37. places:
ROTC A,N
ROT B,-N
ROTC B,N
Thus M AC's can be ROTC'ed in 2M-3 instructions. (Stallman): For 73.
> N > 35.:
ROTC A,N-36
EXCH A,C
ROT B,36.-N
ROTC A,N-72.
ITEM 23 (Gosper, Freiberg):
;B gets 7 bit character in A with even parity
IMUL A,[2010040201] ;5 adjacent copies
AND A,[21042104377] ;every 4th bit of left 4 copies + right copy
IDIVI A,17 ;casting out 15.'s in hexadecimal shifted 7
;odd parity on 7 bits (Schroeppel)
IMUL A,[10040201] ;4 adjacent copies
IOR A,[755555540] ;leaves every 3rd bit+offset+right copy
IDIVI A,9 ;powers of 2^3 are +- mod 9
;changing 7555555400 to 27555555400 gives even parity
;if A is a 9 bit quantity, B gets number of 1's (Schroeppel)
IMUL A,[1001001001] ;4 copies
AND A,[42104210421] ;every 4th bit
IDIVI A,17 ;casting out 15.'s in hexadecimal
;if A is 6 bit quantity, B gets 6 bits reversed (Schroeppel)
IMUL A,[2020202] ;4 copies shifted
AND A,[104422010] ;where bits coincide with reverse repeated base 2^8
IDIVI A,377 ;casting out 2^8 -1's
;reverse 7 bits (Schroeppel)
Page 9
IMUL A,[10004002001] ;4 copies set by 000's base 2 (may set arith. o'flow)
AND A,[210210210010] ;where bits coincide with reverse repeated base 2[8]
IDIVI A,377 ;casting out 377's
;reverse 8 bits (Schroeppel)
MUL A,[100200401002] ;5 copies in A and B
AND B,[20420420020] ;where bits coincide with reverse repeated base 2[10]
ANDI A,41 ;"
DIVI A,1777 ;casting out 2[10]-1's
ITEM 24 (PDP-1 hackers):
foo, lat /DATAI switches
adm a /ADDB
and (707070
adm b
iot 14 /output AC sign bit to a music flip-flop
jmp foo
Makes startling chords, arpeggios, and slides, with just the sign of
the AC. This translates to the PDP-6 (roughly) as:
FOO: DATAI 2
ADDB 1,2
AND 2,[707070707070] ;or 171717171717, 363636363636, 454545454545, ...
ADDB 2,3
LDB 0,[360600,,2]
JRST FOO
Listen to the square waves from the low bits of 0.
ITEM 25 (in order of one-ups-manship: Gosper, Mann, Lenard, [Root and
Mann]):
To count the ones in a PDP-6/10 word:
LDB B,[014300,,A] ;or MOVE B,A then LSH B,-1
AND B,[333333,,333333]
SUB A,B
LSH B,-1
AND B,[333333,,333333]
SUBB A,B ;each octal digit is replaced by number of 1's in it
LSH B,-3
ADD A,B
AND A,[070707,070707]
IDIVI A,77 ;casting out 63.'s
These ten instructions, with constants extended, would work on word
lengths up to 62.; eleven suffice up to 254..
ITEM 26 (Jensen):
Page 10
Useful strings of non-digits and zeros can arise when carefully chosen
negative numbers are fed to unsuspecting decimal print routines.
Different sets arise from different methods of character-to-digit
conversion.
Example (Gosper):
DPT: IDIVI F,12
HRLM G,(P) ;tuck remainder on pushdown list
SKIPE F
PUSHJ P,DPT
LDB G,[220600,,(P)] ;retrieve low 6 bits of remainder
TRCE G,"0 ;convert digit to character
SETOM CCT ;that was no digit!
TYO: .IOT TYOCHN,G ;or DATAO or IDPB ...
AOS G,CCT
POPJ P,
This is the standard recursive decimal print of the positive number in
F, but with a LDB instead of a HLRZ. It falls into the typeout
routine which returns in G the number of characters since the last
carriage return. When called with -36., DPT types carriage return,
line feed, and resets CCT, the character position counter.
ITEM 27 (Gosper):
Since integer division can never produce a larger quotient than
dividend, doubling the dividend and divisor beforehand will
distinguish division by zero from division by 1 or anything else, in
situations where division by zero does nothing.
ITEM 28 (Gosper):
The fundamental operation for building list structure, called CONS, is
defined to: find a free cell in memory, store the argument in it,
remove it from the set of free cells, return a pointer to it, and call
the garbage collector when the set is empty. This can be done in two
instructions:
CONS: EXCH A,[EXCH A,[...[PUSHJ P,GC]...]]
EXCH A,CONS
Of course, the address-linked chain of EXCH's indicated by the nested
brackets is concocted by the garbage collector. This method has the
additional advantage of not constraining an accumulator for the free
storage pointer.
UNCONS: HRLI A,(EXCH A,)
EXCH A,CONS
EXCH A,@CONS
Returns cell addressed by A to free storage list; returns former cell
Page 11
contents in A.
ITEM 29 (Gosper):
The incantation to fix a floating number is usually
MULI A,400 ;exponent to A, fraction to A+1
TSC A,A ;1's complement magnitude of excess 200 exponent
ASH A+1,-200-27.-8(A) ;answer in A+1
If number is known positive, you can omit the TSC. On the PDP-10
UFA A,[+-233000,,] ;not in PDP-6 repertoire
TLC A+1,233000 ;if those bits really bother you
When you know the sign of A, and |A| < 2^(26), you can
FAD A,[+-233400,,] ;or FADR for rounded fix!
TLC A,233400 ;if those bits are relevant
where the sign of the constant must match A's. This works on both
machines and doesn't involve A+1. On the 10, FADRI saves a cycle and
a constant, and rounds.
ITEM 30 (Gosper, Nelson):
21963283741. = 243507216435 is a fixed point of the float function on
the PDP-6/10, i.e., it is the only positive number whose floating
point representation equals its fixed.
ITEM 31 (Gosper):
To get the next higher number (in A) with the same number of 1 bits:
(A, B, C, D do not have to be consecutive)
MOVE B,A
MOVN C,B
AND C,B
ADD A,C
MOVE D,A
XOR D,B
LSH D,-2
IDIVM D,C
IOR A,C
Page 12
ITEM 32 (Gosper):
The "banana phenomenon" was encountered when processing a character
string by taking the last 3 letters typed out, searching for a random
occurrence of that sequence in the text, taking the letter following
that occurrence, typing it out, and iterating. This ensures that
every 4-letter string output occurs in the original. The program
typed ANANANANANANANA.... We note an ambiguity in the phrase, "the
Nth occurrence of." In one sense, there are five 00's in 0000000000;
in another there are nine. The editing program TECO finds five. Thus
it finds only the first ANA in BANANA, and is thus obligated to type N
next. By Murphy's Law, there is but one NAN, thus forcing A, and thus
a loop. An option to find overlapped instances would be useful,
although it would require backing up N-1 characters before seeking the
next N character string.
ITEM 33 (Gosper): DRAWING CURVES INCREMENTALLY
Certain plotters and displays are constrained to approximate curves by
a sequence of king-moves between points on a lattice.
Many curves and contours are definable by F(X,Y) = 0 with F changing
sign on opposite sides of the curve. The following algorithm will
draw most such curves more accurately than polygonal approximations
and more easily than techniques which search for a "next" X and Y just
one move away.
We observe that a good choice of lattice points is just those for
which F, when evaluated on one of them, has opposite sign and smaller
magnitude than on one or more of its four immediate neighbors.[+] This
tends to choose the nearer endpoint of each graph paper line segment
which the curve crosses, if near the curve F is monotone with distance
from the curve.
First, divide the curve into arcs within which the curve's tangent
lies within one 45 degree semiquadrant. We can show that for
reasonable F, only two different increments (say north and northwest)
are needed to visit the desired points.
Thus, we will be changing one coordinate (incrementing Y) every step,
and we have only to check whether changing the other (decrementing X)
will reduce the magnitude of F. (If F increases with Y, F(X,Y+1) >
-F(X-1,Y+1) means decrement X.) F can often be manipulated so that the
inequality simplifies and so that F is easily computed incrementally
from X and Y.
As an example, the following computes the first semiquadrant of the
circle
F = X^2+Y^2-R^2 = 0.
C0: F <- 0, Y <- 0, X <- R
C1: F <- F+2Y+1, Y <- Y+1
Page 13
C2: if+ F +> X, F <- F-2X+1, X <- X-1
C3: if Y < X-1, go to C1
C4: (Link to next arc) if Y = X-1, Y <- Y+1, X <- X-1
This can be bummed by maintaining Z = 2Y+1 instead of Y. Symmetry may
be used to compute all eight semiquadrants at once, or the loop may be
closed at C2 and C3 with two PUSHJ's to provide the palindrome of
decisions for the first quadrant. There is an expression for the
number of steps per quadrant, but it has a three-way conditional
dependent upon the midpoint geometry. Knowing this value, however, we
can replace C3 and C4 with a simple loop count and an odd-even test
for C4.
The loop must be top-tested (C3 before C1) if the "circle" R =1, with
four diagonal segments, is possible.
All this suggests that displays might be designed with an increment
mode which accepts bit strings along with declarations of the form:
"0 means north, 1 means northwest". 1100 (or 0011) will not occur
with a curve of limited curvature; thus, it could be used as an
escape code, but this would be an annoying restriction.
See the following illustration of circles drawn this way.
[+] In case of a tie, i.e., F has equal magnitudes with opposite signs
on adjacent points, do not choose both points but rather have some
arbitrary yet consistent preference for, say, the outer one. The
problem can't arise for C2 in the example because the inequality F >=
X is really F > -(F-2X+1) or F > X-.5.
Page 14
ITEM 34 (Schroeppel, Salamin):
Suppose Y satisfies a differential equation of the form
P(X)Y(Nth derivative) + ..... + Q(X) =R(X)
where P, ..... Q, and R are polynomials in X (for example, Bessel's
equation, X^2Y''+XY'+(X^2-N^2)Y = 0) and A is an algebraic number.
Then Y(A) can be evaluated to N places in time proportional to N(ln
N)^3.
Further, e^X and ln X or any elementary function can be evaluated to N
places in n(ln N)^2 for X a real number. If F(X) (by Newton's
method), and the first derivative of F(X). Also zeta(3) and gamma can
be done in N(ln N)^3.
ITEM 35 (Gosper):
A program which searches a character string for a given substring can
always be written by iterating the sequence fetch-compare-transfer
(ILDB-CAIE-JRST on the PDP6/10) once for each character in the sought
string. The destinations of the transfers (address fields of the
JRST's) must, however, be computed as functions of the sought string.
Let
0 1 2 3 4
S A S S Y
0 1 0 2 2
stand for the program
T0: ILDB C,A ;C gets next char from pointer in A
T1: CAIE C,"S ;skip if it's an S
JRST TO ;loop back on failure
ILDB C,A ;next
T2: CAIE C,"A ;skip if A
JRST T1 ;could be an S
ILDB C,A
T3: CAIE C,"S
JRST TO ;S, A, non S, so start over
ILDB C,A ;next
T4: CAIE C,"S
JRST T2 ;could be SAS.ASSY
ILDB C,A
CAIE C,"Y
JRST T2 ;could be SASS.ASSY
;found SASSY
Page 15
In other words, a number > 0 in the top row is a location in the
program where the corresponding letter of the middle row is compared
with a character of th input string. If it differs, the number in the
bottom row indicates the location where comparison is to resume. If
it matches, the next character of the middle row is compared with the
next character of the input string.
Let J be a number in the top row and K be the number below J, so that
TK is the address of the Jth JRST. For each J = 1, 2, ... we compute
K(J) as follows:
K(1) = 0. Let P be a counter, initially 0.
For each succeeding J, increment P. If the Pth letter = the Jth, K(J)
= K(P). Otherwise, K(J) = P, and P is reset to 0. (P(J) is the
largest number such that the first P characters match the last P
characters in the first J characters of the sought string.)
J= 0 1 0 1 2 3 4 5
M I S S I S S I P P I I S S I S S I P P I
K(J)= 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 0 5 1 0
0 1 2 3 0 1 2 3
C O C A C O L A S A S S A F R A S
0 1 0 2 0 1 3 1 0 1 0 2 1 3 1 1 0
To generalize this method to search for N strings at once, we produce
a program of ILDB-CAIE=JRST's for each of the sought strings, omitting
the initial ILDB from all but the first. We must compute the
destination of the Jth JRST in the Ith program, TKM(I,J) which is the
location of the Kth compare in the Mth program.
It might be reasonable to compile such an instruction sequence
whenever a search is initiated, since alternative schemes usually
require saving or backing up the character pointer.
ITEM 36 (Gosper):
A problem which may arise in machine processing of visual information
is the identification of corners on a noisy boundary of a polygon.
Assume you have a broken line. If it is a closed loop, find the
vertex furthest from the centroid (or any place). Open the loop by
making this place both endpoints and calling it a corner. We define
the corner of a broken line segment to be the point the sum of whose
distances from the endpoints is maximal. This will divide the segment
in two, allowing us to proceed recursively, until our corner isn't
much cornerier than the others along the line.
The perpendicular distance which the vector C lies from the line
connecting vectors A and B is just
(C - A) x (B - A)
------------------- ,
2 |A - B|
Page 16
but maximizing this can lose on very pointy V's. The distance sum
hack can lose on very squashed Z's.
ITEM 37 (@Hurley: origin unknown)
Random number generation:
;Initialize random-number generation
GTAD ;Get current time and date
MOVEM T1,SEED ;Make it the seed
...
CALL RANDOM ;Go get a random number
...
;Return with psuedo random number in T2
RANDOM: MOVE T1,SEED ;Get the seed
MUL T1,RAN ;Make next random number
MOVEM T2,SEED ;Save low order as new seed
IDIVI T1,10 ;Get random number in range 0-7
RET
RAN: 343277,,244615 ;Low order portion of 5^15
SEED: 0
|