;»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
;»»»»»
;»»»»»  $Header: Big:Programming/Assembler/RCS/Random250.asm,v 1.11 1996/06/30 16:52:56 AGMS Exp $
;»»»»»
;»»»»»  This library implements the R250 random number generator.  See "A Fast
;»»»»»  Pseudo Random Number Generator" by W. L. Maier, Dr. Dobbs Journal, May
;»»»»»  1991 page 152 or look at the original by S. Kirkpatrick and E. Stoll
;»»»»»  in The Journal of Computational Physics, volume 40 (1981), page 517.
;»»»»»
;»»»»»  Library Framework by Dennis Jacobfeuerborn.  Thanks for typing in the
;»»»»»  sample library example at the back of the ROM Kernel Reference Manual:
;»»»»»  Libraries.  The bug of using a private variable for the expunge flag
;»»»»»  bit rather than the bit in the library flags field of the standard
;»»»»»  library base has been fixed in this library.
;»»»»»
;»»»»»  This implementation by Alexander G. M. Smith, partly as a learning
;»»»»»  experience for writing Amiga shared libraries.
;»»»»»
;»»»»»  $Log: Random250.asm,v $
;»»»»»  Revision 1.11  1996/06/30  16:52:56  AGMS
;»»»»»  Fix up program comments.
;»»»»»
;»»»»»  Revision 1.10  1996/06/30  15:54:47  AGMS
;»»»»»  Rotate constant seed values so that the high bits get some of the values,
;»»»»»  otherwise a lot of seed numbers are low integers and it takes a lot longer
;»»»»»  to get a smooth stream of random numbers.
;»»»»»
;»»»»»  Revision 1.9  1996/06/30  15:39:28  AGMS
;»»»»»  Fixed up initialisation to be more random and more time based.
;»»»»»
;»»»»»  Revision 1.8  1996/06/27  16:52:36  AGMS
;»»»»»  Most of the code is present now, just needs testing.
;»»»»»
;»»»»»  Revision 1.7  1996/06/25  12:16:35  AGMS
;»»»»»  Under construction.
;»»»»»
;»»»»»  Revision 1.6  1996/06/22  13:52:02  AGMS
;»»»»»  Now checks exec.library version and displays an error message if
;»»»»»  it is too old (need semaphores).
;»»»»»
;»»»»»  Revision 1.5  1996/06/20  21:50:09  AGMS
;»»»»»  First working version of a library.  Numbers aren't random but they
;»»»»»  do work (global state survives program to program runs, except when
;»»»»»  flushed from memory).
;»»»»»
;»»»»»  Revision 1.4  1996/06/20  19:15:26  AGMS
;»»»»»  Simplified includes so it assembles a lot faster.  Also use standard
;»»»»»  CBM macros for structures.
;»»»»»
;»»»»»  Revision 1.3  1996/06/20  17:13:55  AGMS
;»»»»»  Library guts now in place.
;»»»»»
;»»»»»  Revision 1.2  1996/06/16  17:45:39  AGMS
;»»»»»  Fiddling with RCS header stuff.
;»»»»»
;»»»»»  Revision 1.1  1996/06/16  17:41:55  AGMS
;»»»»»  Initial revision
;»»»»»
;»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»

                SECTION random250,CODE

;Begin »»»»» INCLUDES «««««««««««««««««««««««««««««««««««««««««««««««««««««««««
                Include Exec/Types.i
                Include Exec/Initializers.i
                Include Exec/Resident.i
                Include Exec/Semaphores.i
                Include Exec/Libraries.i
                Include R250LVOs.i
;End
;Begin »»»»» Random Number Generator Parameters «««««««««««««««««««««««««««««««
HISTORYSIZE     = 250 ; This many random numbers are stored in the history.
EXTRACTINDEX    = 103 ; Index of the second number to take from the history.
; The next number is generated by XORing together the bits (same as adding and
; ignoring the carry) from the EXTRACTINDEX'th iteration in the past with the
; bit from HISTORYSIZE iterations in the past.  The EXTRACTINDEX and
; HISTORYSIZE should be co-prime (I think) to avoid shortening the random
; sequence.  The reference paper describes values of 250 and 103, and talks
; about the representative polynomial being primitive (so X**103 + X**250
; shouldn't have any factors other than 1 and itself?  In base 2?).
;End
;Begin »»»»» STRUCTURES «««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Our library base and its associated global variables.

                STRUCTURE Random250Base,LIB_SIZE ; Standard lib base at start.
                  APTR    SegList ; Our program's segment list.
                  APTR    ExecBase ; Our exec.library reference, zero if closed.
                  APTR    IntuitionBase ; Our intuition.library, zero if closed.
                  STRUCT  R250Semaphore,SS_SIZE ; Guards shared history memory.
                  STRUCT  HistoryArray,(HISTORYSIZE*4) ; Numbers are 4 bytes.
                  LABEL   HistoryArrayEnd
                  ULONG   RandomWordForUser
                  APTR    HistoryPntr ; Points into HistoryArray, next number
                                      ; will go here, contains oldest number
                                      ; in the history before it's overwritten.
                LABEL Random250Base_SIZE
;End

;Begin »»»»» Dummy Startup-code «««««««««««««««««««««««««««««««««««««««««««««««
DummyStart      Moveq   #-1,d0  ; Don't even try to run me !!!  Makes the
                Rts             ; CLI return a not-a-program error if you try.
;End
;Begin »»»»» ROMTag Structure «««««««««««««««««««««««««««««««««««««««««««««««««
ROMTag          Dc.w    RTC_MATCHWORD   ;uword  rt_matchword
                Dc.l    ROMTag          ;aptr   rt_matchtag
                Dc.l    EndOfLib        ;aptr   rt_endskip
                Dc.b    RTF_AUTOINIT    ;ubyte  rt_flags
                Dc.b    VERSION         ;ubyte  rt_version
                Dc.b    NT_LIBRARY      ;ubyte  rt_type
                Dc.b    0               ;ubyte  rt_pri
                Dc.l    LibraryName     ;aptr   rt_name
                Dc.l    LibraryID       ;aptr   rt_idstring
                Dc.l    InitTable       ;aptr   rt_init
;End
;Begin »»»»» Library Names, Versions & ID «««««««««««««««««««««««««««««««««««««
VERSION         =       1
REVISION        =       0
LibraryName     Dc.b    "random250.library",0
LibraryID       Dc.b    "random250 1.0 (16.6.96)",13,10,0
                Dc.b    "$VER: random250 1.0 (16.6.1996) (by Alexander G. M. Smith)",0
                Dc.b    10,10,"$Id: Random250.asm,v 1.11 1996/06/30 16:52:56 AGMS Exp $",10
                Dc.b    "Public domain 1996 by Alexander G. M. Smith, all rights unreserved.",10
                Dc.b    "Send questions to agmsmith@achilles.net, agmsmith@bix.com,",10
                Dc.b    "au829@freenet.carleton.ca and 71330.3173@CompuServe.com",10,10,0
ExecName        Dc.b    "exec.library",0
IntuitionName   Dc.b    "intuition.library",0
                Even
;End
;Begin »»»»» Library Initialisation Table «««««««««««««««««««««««««««««««««««««
InitTable       Dc.l    Random250Base_SIZE      ;structure size (library base)
                Dc.l    FunctionTable           ;function list
                Dc.l    LibBaseData             ;information for initializing
                Dc.l    InitRoutine             ;own routine for initialization
;End
;Begin »»»»» Library Function Table «««««««««««««««««««««««««««««««««««««««««««
FunctionTable   Dc.l    Open                    ; The basic lib functions
                Dc.l    Close
                Dc.l    Expunge
                Dc.l    NullFunc
; Place your functions after here !
                Dc.l    GenerateRandomNumber    ; One random 32 bit number.
                Dc.l    GenerateRandomArray     ; An array of 32 bit randoms.
                Dc.l    -1                      ; End of the function list.
;End
;Begin »»»»» LibBaseData ««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Special mini-program that initialises the library base structure as part of
; the ROMTag processing (see the InitStruct function).  Unspecified fields will
; be cleared to zero.
LibBaseData     INITBYTE        LN_TYPE,NT_LIBRARY
                INITLONG        LN_NAME,LibraryName
                INITBYTE        LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
                INITWORD        LIB_VERSION,VERSION
                INITWORD        LIB_REVISION,REVISION
                INITLONG        LIB_IDSTRING,LibraryID
                Dc.l            0       ;end!
;End
;Begin »»»»» InituiTexts for error requester «««««««««««««««««««««««««««««««««««
ExecVersionIText
                Dc.b    0 ; it_FrontPen, the pens for rendering the text
                Dc.b    0 ; it_BackPen, the pens for rendering the text
                Dc.b    0 ; it_DrawMode, the mode for rendering the text
                Dc.b    0 ; it_KludgeFill00, This is strictly for word-alignment
                Dc.w    16 ; it_LeftEdge, relative start location for the text
                Dc.w    15 ; it_TopEdge, relative start location for the text
                Dc.l    0 ; it_ITextFont, if NULL, you accept the defaults
                Dc.l    ExecVersionText ; it_IText, pointer to null-terminated text
                Dc.l    0 ; it_NextText, pointer to another IntuiText to render

CancelIText     Dc.b    0 ; it_FrontPen, the pens for rendering the text
                Dc.b    0 ; it_BackPen, the pens for rendering the text
                Dc.b    0 ; it_DrawMode, the mode for rendering the text
                Dc.b    0 ; it_KludgeFill00, This is strictly for word-alignment
                Dc.w    6 ; it_LeftEdge, relative start location for the text
                Dc.w    3 ; it_TopEdge, relative start location for the text
                Dc.l    0 ; it_ITextFont, if NULL, you accept the defaults
                Dc.l    CancelText ; it_IText, pointer to null-terminated text
                Dc.l    0 ; it_NextText, pointer to another IntuiText to render

ExecVersionText Dc.b    "Random250.library requires AmigaDOS v2.0+ (V36+)",0
CancelText      Dc.b    "Cancel",0
                Even
;End
;Begin »»»»» InitRoutine ««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Library has just been allocated, library base pointer in D0, segment list
; pointer in A0, execbase in A6.  System is in a Forbid (non-multitasking)
; state.  Returns zero in D0 on failure, returns library base address if
; successful (will make system add the library to the loaded libraries list).
InitRoutine     Movem.l d2-d7/a2-a6,-(sp)
                Move.l  d0,a5   ; Get our library base record address.
                Move.l  a0,SegList(a5)

; Open intuition.library (any version, so it works under AmigaDOS 1.3 too),
; just in case we need to display error messages.  Also needed for getting
; the time of day for random number seeding.
                Lea     IntuitionName(PC),a1
                Move.l  #0,d0   ; Any version.
                Jsr     _LVOOpenLibrary(a6)
                Move.l  d0,IntuitionBase(a5)
                Beq     FailedInit  ; Oops!  Can't even display a message.

; Open exec.library version 36 or better (need SignalSemaphores).
                Lea     ExecName(PC),a1
                Move.l  #36,d0  ; Version 36 or better.
                Jsr     _LVOOpenLibrary(a6)
                Move.l  d0,ExecBase(a5)
                Bne     ExecOpened  ; If open was successful.

; Exec.library open failed, display an error message.
                Move.l  #0,d0   ; No OK button flags.
                Move.l  d0,a0   ; No window specified.
                Lea     ExecVersionIText(PC),a1 ; Body iText.
                Move.l  d0,a2   ; No OK button iText.
                Lea     CancelIText(PC),a3 ; Cancel button iText.
                Move.l  d0,d1   ; No Cancel button flags.
                Move.l  #320,d2 ; Width of requester.
                Move.l  #72,d3  ; Height of requester.
                Move.l  a6,a4   ; Save someone else's ExecBase.
                Move.l  IntuitionBase(a5),a6
                Jsr     _LVOAutoRequest(a6)
                Move.l  a4,a6   ; Restore someone else's ExecBase.
                Bra     FailedInit

; Exec was opened, now initialise our semaphore.
ExecOpened      Move.l  d0,a6   ; Now using our exec.library.
                Lea     R250Semaphore(a5),a0
                Jsr     _LVOInitSemaphore(a6)

; Set up the initial random number seed.  It consists of a copy of ExecBase
; (each long word rotated differently to make it more randomish).  ExecBase is
; used because it has various timers and counters as part of its dynamic data.
; Followed by an upper right triangular pattern of bits (for linear
; independence).  Followed by stuff copied from the the code for this library
; (again rotated for randomness).  Followed lastly by 32 bytes (8 long words)
; of the current real time (rotated some more so all bits get some of the
; least significant time bits).
ExecBaseSize    = $268  ; Size of ExecBase for V36 of AmigaDOS.
                Move.l  ExecBase(a5),a0
                Move.w  #ExecBaseSize/4-1,d0  ; V36 ExecBase size in longs-1.
                IFLE    HISTORYSIZE-ExecBaseSize/4-32-8
                Fail    "Bug: history size is smaller than minimum seed size."
                ENDC
                Lea     HistoryArray(a5),a1
                Move.l  a1,HistoryPntr(a5) ; Init starting number pointer too.
InitHistoryLoop Move.l  (a0)+,d1
                Rol.l   d0,d1  ; Rotate so don't get lots of zero high bits.
                Move.l  d1,(a1)+
                Dbra    d0,InitHistoryLoop

; Now put in the triangular matrix-like part for linear independence.  It
; has 32 long words with a pattern of ones shifting over to zeroes.
                Move.l  #-1,d0  ; All 32 bits turned on.
TriangleInit    Move.l  d0,(a1)+
                Lsr.l   #1,d0   ; Shift right one bit, fill with zeroes.
                Bne     TriangleInit ; Still some one bits remaining.

; Fill a variable sized part with a copy of the code for this program.  Amount
; to copy is HISTORYSIZE long words minus size of ExecBase (in long words),
; minus size of triangular matrix (32 long words), minus real time size (8
; long words).  Also rotate the long words for extra randomness.
                Lea     DummyStart(PC),a0
                Move.l  #HISTORYSIZE-ExecBaseSize/4-32-8-1,d0
CodeCopyLoop    Move.l  (a0)+,d1
                Rol.l   d0,d1  ; Rotate so don't get lots of zero high bits.
                Move.l  d1,(a1)+
                Dbra    d0,CodeCopyLoop

; Get the real time, microseconds (A1) and seconds (A0), from Intuition.
                Lea     4(a1),a0
                Move.l  a1,a2  ; Save for later.
                Move.l  IntuitionBase(a5),a6
                Jsr     _LVOCurrentTime(a6)

; Also put in rotated time words so that high bytes and other bits
; are affected by the time.
                Lea     8(a2),a1  ; Point after last written values.
                Move.l  (a2)+,d0  ; Read a time long word.  Microseconds
                Rol.l   #8,d0
                Move.l  d0,(a1)+
                Rol.l   #8,d0
                Move.l  d0,(a1)+
                Rol.l   #8,d0
                Move.l  d0,(a1)+

                Move.l  (a2)+,d0  ; Read the other time long word.  Seconds.
                Rol.l   #8,d0
                Move.l  d0,(a1)+
                Rol.l   #8,d0
                Move.l  d0,(a1)+
                Rol.l   #8,d0
                Move.l  d0,(a1)+

; Now run through HISTORYSIZE*HISTORYSIZE random numbers to mix the seed
; values thoroughly.  That way the current time seed will influence all
; of the seed so that you don't get a bunch of the same values then
; one or two new ones then a bunch of the same again.
                Move.w  #HISTORYSIZE-1,d0
InitSeriesLoop  Lea     HistoryArray(a5),a0
                Lea     EXTRACTINDEX*4(a0),a1
                Move.w  #HISTORYSIZE-EXTRACTINDEX-1,d1
InitFirstHalf   Move.l  (a1)+,d2
                Eor.l   d2,(a0)+
                Dbra    d1,InitFirstHalf

                Lea     HistoryArray(a5),a1
                Move.w  #EXTRACTINDEX-1,d1
InitSecondHalf  Move.l  (a1)+,d2
                Eor.l   d2,(a0)+
                Dbra    d1,InitSecondHalf

                Dbra    d0,InitSeriesLoop

; Return with D0 non-zero (set to our library base) for success.
                Move.l  a5,d0
                Movem.l (sp)+,d2-d7/a2-a6
                Rts

; Failed to load and initialise this library, deallocate library base record
; and return zero in D0 (so library isn't added to the system).
FailedInit      Bsr     FreeLibraryBase ; Deallocate base (A5=base,A6=exec).
                Movem.l (sp)+,d2-d7/a2-a6
                Moveq   #0,d0
                Rts
;End
;Begin »»»»» FreeLibraryBase ««««««««««««««««««««««««««««««««««««««««««««««««««
; Subroutine to free our library base's memory.  Also closes libraries that
; we have opened.  Library base in A5, ExecBase in A6.
FreeLibraryBase Move.l  IntuitionBase(a5),d0
                Beq     IntuitionClosed  ; Do nothing if not opened.
                Move.l  d0,a1
                Jsr     _LVOCloseLibrary(a6) ; Close intuition.library.
                Clr.l   IntuitionBase(a5)
IntuitionClosed Move.l  ExecBase(a5),d0
                Beq     ExecClosed  ; Do nothing if not opened.
                Move.l  d0,a1
                Jsr     _LVOCloseLibrary(a6) ; Close exec.library.
                Clr.l   ExecBase(a5)
ExecClosed      Moveq   #0,d0   ; Add up size of our library base record.
                Move.l  a5,a1
                Move.w  LIB_NEGSIZE(a5),d0 ; It is unsigned short.
                Sub.l   d0,a1   ; Get actual start of record.
                Add.w   LIB_POSSIZE(a5),d0 ; Shouldn't overflow, I hope.
                Jmp     _LVOFreeMem(a6) ; Deallocate our library base record.
;End
;Begin »»»»» Open «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Our library base is in A6.  Return 0 if we can't handle another instance of
; this library being opened, else return the library base if successful.  The
; system is in a forbid state.
Open            Addq.w  #1,LIB_OPENCNT(a6)  ; One more active user of this library.
                BClr    #LIBB_DELEXP,LIB_FLAGS(a6) ; Disable a delayed expunge.
                Move.l  a6,d0
                Rts
;End
;Begin »»»»» Close ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Close the library.  Decrement the open count, but never let it go below zero.
; Returns the segment list if the library is expunged as a side effect of the
; close, otherwise just returns zero.
Close           Move.w  LIB_OPENCNT(a6),D0
                Beq.s   CloseCountDone  ; Don't let the count go below zero.
                Subq.w  #1,D0
                Move.w  D0,LIB_OPENCNT(a6) ; One less user of this library.
CloseCountDone  Bne.s   Close_NotLast ; If still more users remaining.

                BTst    #LIBB_DELEXP,LIB_FLAGS(a6)
                Bne.s   Expunge ; Fulfill the outstanding expunge.

Close_NotLast   Moveq   #0,d0
                Rts
;End
;Begin »»»»» Expunge ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Deallocate this library's data if nobody is using it.  If it is in use, do a
; delayed expunge later when nobody is using the library.  Returns the segment
; list if the library was deallocated, zero if still in use.  The usual Forbid
; is in progress, and this can also be called by the OS memory routines so
; don't do Wait or other things.  This can also be called by RemLibrary.
Expunge         Tst.w   LIB_OPENCNT(a6)
                Beq.s   Expunge_Ok  ; If nobody using our library right now.
                BSet    #LIBB_DELEXP,LIB_FLAGS(a6) ; Request expunge later.
                Moveq   #0,d0
                Rts

Expunge_Ok      Movem.l d2/a5/a6,-(sp)
                Move.l  a6,a5  ; Save our library base address.
                Move.l  ExecBase(a5),a6
                Move.l  SegList(a5),d2  ; Get value before base record deleted.

                Move.l  a5,a1
                Jsr     _LVORemove(a6) ; Removes node specified by A1 from a list.
                Bsr     FreeLibraryBase ; Deallocate base (A5=base,A6=exec).

                Move.l  d2,d0   ; Return code is our segment list.
                Movem.l (sp)+,d2/a5/a6
                Rts
;End
;Begin »»»»» Null «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; For unimplemented function slots.  Like that reserved one that returns zero.
NullFunc        Moveq   #0,d0
                Rts
;End
        ;*** your routines starting from -30 ...

;Begin »»»»» GenerateRandomNumber «««««««««««««««««««««««««««««««««««««««««««««
; Generate just one random long word, returned in D0.  Not very efficient to
; call, so ask for a batch of numbers if you want to be efficient.
GenerateRandomNumber:
                Move.l  a6,a1  ; Save our library base while calling exec.
                Move.l  ExecBase(a1),a6
                Lea     R250Semaphore(a1),a0
                Jsr     _LVOObtainSemaphore(a6) ; Doesn't change registers.
                Move.l  a1,a6
                Lea     RandomWordForUser(a6),a0
                Move.l  #1,d0
                Bsr     GenerateRandomArray
                Move.l  RandomWordForUser(a6),d0
                Move.l  a6,a1
                Move.l  ExecBase(a1),a6
                Lea     R250Semaphore(a1),a0
                Jsr     _LVOReleaseSemaphore(a6) ; Doesn't change registers.
                Move.l  a1,a6
                Rts
;End
;Begin »»»»» GenerateRandomArray ««««««««««««««««««««««««««««««««««««««««««««««
; Fills an array with random long words.
; Inputs: D0 is number of desired random long words (you can ask for more
; than 64K numbers if you want to), A0 points to the start of the array,
; A6 points to the library base.
; Each number is generated by XORing the long word at HistoryPntr (which is
; the random number from HISTORYSIZE iterations ago) with the long word at
; HistoryPntr+EXTRACTINDEX*4 modulo the history array size (the random number
; from EXTRACTINDEX iterations ago).  The new number is stored at HistoryPntr
; and HistoryPntr is incremented modulo the array size.
GenerateRandomArray:
TempDR          EQUR    d0
WrapCountDR     EQUR    d1
UserCountDR     EQUR    d2  ; Must be d2 to safely allow exec.library calls.
ArrayStartAR    EQUR    a0
ArrayEndAR      EQUR    a1
OutputBufferAR  EQUR    a2  ; Must be a2-a5 for library call safety.
NewOldNumberAR  EQUR    a3
MiddleNumberAR  EQUR    a4
R250BaseAR      EQUR    a5
                Movem.l d2/a2-a6,-(sp)

; Save input arguments in unchanged registers while we acquire a semaphore.
; Need the semaphore so some other task doesn't start changing the random
; number seed and index while this one is generating numbers.
                Move.l  a0,OutputBufferAR
                Move.l  d0,UserCountDR
                Move.l  a6,R250BaseAR  ; Save our library base.
                Move.l  ExecBase(R250BaseAR),a6
                Lea     R250Semaphore(R250BaseAR),a0
                Jsr     _LVOObtainSemaphore(a6)

; Set up the starting and constant values of various register variables.
                Lea     HistoryArrayEnd(R250BaseAR),ArrayEndAR
                Lea     HistoryArray(R250BaseAR),ArrayStartAR
                Move.l  HistoryPntr(R250BaseAR),NewOldNumberAR
                Lea     EXTRACTINDEX*4(NewOldNumberAR),MiddleNumberAR
                Cmp.l   ArrayEndAR,MiddleNumberAR
                Bcs     MiddleHigher  ; If MiddleNumberAR < ArrayEndAR
                Sub     #HISTORYSIZE*4,MiddleNumberAR ; Middle wrapped now.

; The NewOldNumber is the highest one in absolute memory location, it will be
; the first one to wrap around (hit the end of the HistoryArray) after being
; incremented a few times.  But how many times?  At least one.  And at most
; the history array size (which is less than 64K long words).
NewOldHigher    Move.l  ArrayEndAR,WrapCountDR
                Sub.l   NewOldNumberAR,WrapCountDR
                Lsr.l   #2,WrapCountDR ; Convert to number of long words.
                Cmp.l   UserCountDR,WrapCountDR
                Bhi     PartialEnding ; If amount to do < amount available.

                Sub.l   WrapCountDR,UserCountDR ; Will do this many numbers.
                Bra     NewOldLoopEntry
NewOldLoop      Move.l  (MiddleNumberAR)+,TempDR
                Eor.l   TempDR,(NewOldNumberAR)
                Move.l  (NewOldNumberAR)+,(OutputBufferAR)+
NewOldLoopEntry Dbra    WrapCountDR,NewOldLoop ; Fortunately WrapCount < 64K.

; Ok, the NewOldNumber has hit the end of the array, it wraps and the
; MiddleNumber will be the next one to hit the end.
                Move.l  ArrayStartAR,NewOldNumberAR
; Redundant 6 byte instruction:  Bra     MiddleHigher

; The MiddleNumber is the highest one in absolute memory location, it will be
; the first one to wrap around after being incremented a few times.  Etc.
MiddleHigher    Move.l  ArrayEndAR,WrapCountDR
                Sub.l   MiddleNumberAR,WrapCountDR
                Lsr.l   #2,WrapCountDR ; Convert to number of long words.
                Cmp.l   UserCountDR,WrapCountDR
                Bhi     PartialEnding ; Amount to do < amount available.

                Sub.l   WrapCountDR,UserCountDR ; Will do this many numbers.
                Bra     MiddleLoopEntry
MiddleLoop      Move.l  (MiddleNumberAR)+,TempDR
                Eor.l   TempDR,(NewOldNumberAR)
                Move.l  (NewOldNumberAR)+,(OutputBufferAR)+
MiddleLoopEntry Dbra    WrapCountDR,MiddleLoop

; Ok, the MiddleNumber has hit the end of the array, it wraps and the
; NewOldNumber will be the next one to hit the end.
                Move.l  ArrayStartAR,MiddleNumberAR
                Bra     NewOldHigher

; Get here if the number of numbers available in the array before a wrapping
; point occurs is larger than the number of numbers we were asked for.  So,
; generate away without worrying about wrapping.  Can also treat the counter
; as 16 bits since it has at most the history size in values (since we know
; the pointers won't wrap).  Enter at PartialEnding so that the zero iteration
; case works.
PartialLoop     Move.l  (MiddleNumberAR)+,TempDR
                Eor.l   TempDR,(NewOldNumberAR)
                Move.l  (NewOldNumberAR)+,(OutputBufferAR)+
PartialEnding   Dbra    UserCountDR,PartialLoop
                Move.l  NewOldNumberAR,HistoryPntr(R250BaseAR)

; Release the semaphore and return.
ExitRandomArray Lea     R250Semaphore(R250BaseAR),a0
                Jsr     _LVOReleaseSemaphore(a6)
                Movem.l (sp)+,d2/a2-a6
                Rts
;End

EndOfLib  ; End of the library, so the ROM knows how much needs to be checksummed.
                END
