Jump to content
IGNORED

Seeding the random number generator DPC+


wallaby
 Share

Recommended Posts

OKay, I've read you can seed rand by setting it to a value. Like rand = 30.

 

That will then give you a repeatable pattern of random numbers. This is a powerful feature of random number generators. But, there is an interesting problem with the Atari 2600.

 

Can you only seed the rand function with a byte? So there can only be 256 sequences?

 

Most of the time, a seed with be the number of seconds since 1970 or whatever, so the seed is always different. But you have the option of setting the seed to potentially millions of possible sequences.

 

Is there a way to set the DPC+ seed to a larger number?

Link to comment
Share on other sites

DPC+ uses a 32 bit number, not 8 bit. It's implemented as an LFSR in software. The implementation in Stella, which corresponds to the ARM assembly code used in the DPC+ driver, is:

 

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void CartridgeDPCPlus::clockRandomNumberGenerator()
{
  // Update random number generator (32-bit LFSR)
  myRandomNumber = ((myRandomNumber & (1<<10)) ? 0x10adab1e: 0x00) ^
                   ((myRandomNumber >> 11) | (myRandomNumber << 21));
}

Additionally, the LFSR used is reversible (not all are).

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void CartridgeDPCPlus::priorClockRandomNumberGenerator()
{
  // Update random number generator (32-bit LFSR, reversed)
  myRandomNumber = ((myRandomNumber & (1u<<31)) ?
    ((0x10adab1e^myRandomNumber) << 11) | ((0x10adab1e^myRandomNumber) >> 21) :
    (myRandomNumber << 11) | (myRandomNumber >> 21));
}

A reversable LFSR is useful for things like creating the world in Pitfall where if you exit a screen to the left, then exit that screen to the right, you should end up where you started. You can use demo 3 to see the sequence of random numbers.

 

There are 5 registers for reading the random number:

;----------------------------------------
; Random Numbers
;----------------------------------------
; DPC+ provides a 32 bit LFSR (Linear feedback shift register)
; which is used as a random number generator.  Each individual byte of the
; random number will return values from 0-255.  The random numbers will follow
; an exact sequence, so it's best to clock them at least once per frame even if 
; you don't need the value (this allows the amount of time it takes the user to
; start the game to select a random starting point in the sequence)
;----------------------------------------
RANDOM0NEXT   DS 1    ; $00 clock next 32 bit number and returns byte 0
RANDOM0PRIOR  DS 1    ; $01 clock prior 32 bit number and returns byte 0
RANDOM1       DS 1    ; $02 returns byte 1 of random number w/out clock
RANDOM2       DS 1    ; $03 returns byte 2 of random number w/out clock
RANDOM3       DS 1    ; $04 returns byte 3 of random number w/out clock

The two RANDOM0 registers will advance the value to either the next, or the prior, and return byte 0 of the 32 bit (4 byte) random number. After that you can use RANDOM1, RANDOM2 and RANDOM3 to get the other 3 bytes.

 

 

 

Been a while since I've looked at bB, but I think you could use the values like this:

 a = RANDOM0NEXT
 if (a & 1) then DO_SOMETHING

 a = RANDOM1
 if (a < 128) then DO_SOMETHING_ELSE

...

 a = RANDOM3
 if (a >= 64) then DO_YET_SOMETHING_ELSE


and maybe even this:

 if (RANDOM0NEXT & 1) then DO_SOMETHING
 if (RANDOM1 < 128) then DO_SOMETHING_ELSE
...
 if (RANDOM3 >= 64) then DO_YET_SOMETHING_ELSE

One thing to remember is if you do this:

 a = RANDOM0NEXT
 b = RANDOM0NEXT
 c = RANDOM0NEXT

then a, b, and c will most likely have different values because you told DPC+ to advance the LFSR. However, if you do this:

 a = RANDOM1
 b = RANDOM1
 c = RANDOM1

then a, b, and c are guaranteed to have the same value because you did not tell DPC+ to advance the LFSR.

 

 

There are also 5 registers for seeding the random number:

;----------------------------------------
; Random Number Initialization
;----------------------------------------
; The random number generate defaults to a value that spells out DPC+.
; Store any value to RRESET to set the random number back to DPC+, or you
; can use RWRITE0-3 to change the 32 bit value to anything you desire.
;
; reset random number
;	LDA #0
;	STA RRESET
;
; set a specific random number (spells out 2600)
;	LDA #$32
;	STA RWRITE0
;	LDA #$36
;	STA RWRITE1
;	LDA #$30
;	STA RWRITE2
;	STA RWRITE3
;
; NOTE: do not set all 4 bytes to 0, as that will disable the generator.
;----------------------------------------
RRESET        DS 1    ; $70
RWRITE0       DS 1    ; $71
RWRITE1       DS 1    ; $72
RWRITE2       DS 1    ; $73
RWRITE3       DS 1    ; $74

RRESET will put the value back to the powerup value, assign it any value to trigger the reset:

 RRESET = 1 

RWRITE0 thru RWRITE3 let you assign any value you want, just don't make them all 0s or that will disable the LFSR. As an example:

 RWRITE0 = 11
 RWRITE1 = 9
 RWRITE2 = 19
 RWRITE3 = 66
  • Like 1
Link to comment
Share on other sites

there is no way that the 2600 can know the seconds since 1970 so what does make the seed?

The default seed is:

void CartridgeDPCPlus::setInitialState()
{
...
  // Initialize the DPC's random number generator register (must be non-zero)
  myRandomNumber = 0x2B435044; // "DPC+"
}

The ASCII values are:

  • 0x44 = D
  • 0x50 = P
  • 0x43 = C
  • 0x2B = +
The order is the opposite of you might expect, that's because the ARM (like the 6507) is little-endian.

 

That value shows up twice in the DPC+ and is part of what Stella uses to auto-detect a DPC+ ROM:

bool Cartridge::isProbablyDPCplus(const uInt8* image, uInt32 size)
{
  // DPC+ ARM code has 2 occurrences of the string DPC+
  uInt8 signature[] = { 'D', 'P', 'C', '+' };
  return searchForBytes(image, size, signature, 4, 2);
}
Link to comment
Share on other sites

RWRITE0 thru RWRITE3 let you assign any value you want, just don't make them all 0s or that will disable the LFSR. As an example:

 RWRITE0 = 11
 RWRITE1 = 9
 RWRITE2 = 19
 RWRITE3 = 66

 

A 32 bit random number generator is perfect, but how do you access the registers like RWRITE from bB? Or is it more practical to use assembly and make a macro or something?

 

Finally, a bit off topic - I'd love to use C to program the Atari but one thing that makes bB so attractive is the multitude of examples and help the community provides. Also, Random Terrain's website with just about everything you need all on one page. Are there any resources for C that can kick start Atari development?

 

What are the pros and cons to using C and does programming directly on the arm chip grant you a huge computing resource?

Link to comment
Share on other sites

A 32 bit random number generator is perfect, but how do you access the registers like RWRITE from bB? Or is it more practical to use assembly and make a macro or something?

While I'm not well versed with bB, the examples I gave are what I think you'd use in bB. If those don't work then I'm sure somebody more familiar with bB will step up and show how to access those registers now that they know what they're called.

 

 

Finally, a bit off topic - I'd love to use C to program the Atari but one thing that makes bB so attractive is the multitude of examples and help the community provides. Also, Random Terrain's website with just about everything you need all on one page. Are there any resources for C that can kick start Atari development?

 

What are the pros and cons to using C and does programming directly on the arm chip grant you a huge computing resource?

 

Using C gives you a lot of extra resources; however, you still need to write 6507 assembly code as the two work together hand-in-hand. It's how I was able to pull off Space Rocks and Stay Frosty 2. There's 3 unfinished games as well, Draconian, Frantic and Timmy. Most of the source for my projects has been made available in my blog. To make it easier to find things look in the right hand column for the section labeled Categories.

 

I'd recommend you first start out with my series Collect, which covers writing a 2K game from scratch using 6507 assembly.

 

After you've familiar with that, then check out my DPC+ ARM Development series. Do note it's currently on hiatus as we've been busy working on Bus Stuffing which will eventually let us do things that we can't even do with DPC+.

Link to comment
Share on other sites

Looks like they're called the same in bB. I tried it before and got an error, but I just made a few asm macros and it turns out they're directly accessible in bB using those names. That was excellent, thanks. I also found those comments in the .txt file that bB generates which was a big help too.

Link to comment
Share on other sites

Looks like they're called the same in bB. I tried it before and got an error, but I just made a few asm macros and it turns out they're directly accessible in bB using those names. That was excellent, thanks. I also found those comments in the .txt file that bB generates which was a big help too.

 

 

Great!

 

Those comments are from the DPCplus.h file; though I think the rest of the registers, those not related to random numbers, will not be very useful for bB programmers.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...